diff --git a/src/compiler/_namespaces/ts.ts b/src/compiler/_namespaces/ts.ts index 5a01767f96d44..63e4ae74e6cf4 100644 --- a/src/compiler/_namespaces/ts.ts +++ b/src/compiler/_namespaces/ts.ts @@ -2,16 +2,18 @@ export * from "../corePublic"; export * from "../core"; -export * from "../debug"; +export * from "../extension"; export * from "../semver"; export * from "../performanceCore"; export * from "../perfLogger"; export * from "../tracing"; export * from "../types"; export * from "../sys"; +export * from "../sysUtilities"; export * from "../path"; export * from "../diagnosticInformationMap.generated"; export * from "../scanner"; +export * from "../scannerUtilities"; export * from "../utilitiesPublic"; export * from "../utilities"; export * from "../factory/baseNodeFactory"; @@ -24,10 +26,15 @@ export * from "../factory/nodeTests"; export * from "../factory/utilities"; export * from "../factory/utilitiesPublic"; export * from "../parser"; +export * from "../parserUtilities"; export * from "../commandLineParser"; +export * from "../commandLineParserUtilities"; export * from "../moduleNameResolver"; +export * from "../moduleSpecifiersUtilities"; +export * from "../objectAllocator"; export * from "../binder"; export * from "../symbolWalker"; +export * from "../symlinkCache"; export * from "../checker"; export * from "../visitorPublic"; export * from "../sourcemap"; @@ -69,6 +76,9 @@ export * from "../watch"; export * from "../watchPublic"; export * from "../tsbuild"; export * from "../tsbuildPublic"; +export * from "../fileMatcher"; +import * as Debug from "../debug"; +export { Debug }; import * as moduleSpecifiers from "./ts.moduleSpecifiers"; export { moduleSpecifiers }; import * as performance from "./ts.performance"; diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f54a3e0666f00..e638b47fdc9e6 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1,9 +1,72 @@ import { - __String, - AccessExpression, - addRelatedInfo, append, appendIfUnique, + cast, + concatenate, + contains, + createQueue, + every, + forEach, + getRangesWhere, + isString, + length, + Pattern, + some, + tryCast, +} from "./core"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { removeFileExtension } from "./extension"; +import { createBinaryExpressionTrampoline } from "./factory/binaryExpressionStateMachine"; +import { + isBinaryExpression, + isBlock, + isCallExpression, + isClassStaticBlockDeclaration, + isConditionalTypeNode, + isElementAccessExpression, + isEnumDeclaration, + isExportAssignment, + isExportDeclaration, + isExportSpecifier, + isFunctionDeclaration, + isIdentifier, + isJSDocEnumTag, + isJSDocTemplateTag, + isJsxNamespacedName, + isModuleBlock, + isModuleDeclaration, + isNamespaceExport, + isNonNullExpression, + isObjectLiteralExpression, + isOmittedExpression, + isParenthesizedExpression, + isPrefixUnaryExpression, + isPrivateIdentifier, + isPropertyAccessExpression, + isShorthandPropertyAssignment, + isSourceFile, + isTypeAliasDeclaration, + isTypeOfExpression, + isVariableDeclaration, + isVariableStatement, +} from "./factory/nodeTests"; +import { objectAllocator } from "./objectAllocator"; +import { forEachChild } from "./parser"; +import { + setParent, + setParentRecursive, +} from "./parserUtilities"; +import { perfLogger } from "./perfLogger"; +import * as performance from "./performance"; +import { tokenToString } from "./scanner"; +import { + tracing, + TracingNode, +} from "./tracing"; +import { + __String, + AccessExpression, ArrayBindingElement, ArrayLiteralExpression, ArrowFunction, @@ -21,34 +84,21 @@ import { BreakOrContinueStatement, CallChain, CallExpression, - canHaveLocals, - canHaveSymbol, CaseBlock, CaseClause, - cast, CatchClause, ClassLikeDeclaration, ClassStaticBlockDeclaration, CompilerOptions, - concatenate, ConditionalExpression, ConditionalTypeNode, - contains, - createBinaryExpressionTrampoline, - createDiagnosticForNodeInSourceFile, - createFileDiagnostic, - createQueue, - createSymbolTable, - Debug, Declaration, - declarationNameToString, DeleteExpression, DestructuringAssignment, DiagnosticArguments, DiagnosticCategory, DiagnosticMessage, DiagnosticRelatedInformation, - Diagnostics, DiagnosticWithLocation, DoStatement, DynamicNamedDeclaration, @@ -56,32 +106,107 @@ import { ElementAccessExpression, EntityNameExpression, EnumDeclaration, - escapeLeadingUnderscores, - every, ExportAssignment, - exportAssignmentIsAlias, ExportDeclaration, ExportSpecifier, Expression, ExpressionStatement, - findAncestor, FlowFlags, FlowLabel, FlowNode, FlowReduceLabel, - forEach, - forEachChild, ForInOrOfStatement, ForStatement, FunctionDeclaration, FunctionExpression, FunctionLikeDeclaration, GetAccessorDeclaration, + HasContainerFlags, + HasFlowNode, + HasLocals, + Identifier, + IfStatement, + ImportClause, + InternalSymbolName, + IsBlockScopedContainer, + IsContainer, + JSDocCallbackTag, + JSDocClassTag, + JSDocEnumTag, + JSDocFunctionType, + JSDocOverloadTag, + JSDocParameterTag, + JSDocPropertyLikeTag, + JSDocSignature, + JSDocTypedefTag, + JSDocTypeLiteral, + JsxAttribute, + JsxAttributes, + LabeledStatement, + LiteralLikeElementAccessExpression, + MappedTypeNode, + MetaProperty, + MethodDeclaration, + ModifierFlags, + ModuleBlock, + ModuleDeclaration, + Mutable, + NamespaceExportDeclaration, + Node, + NodeArray, + NodeFlags, + NonNullChain, + NonNullExpression, + ObjectLiteralExpression, + OptionalChain, + ParameterDeclaration, + ParenthesizedExpression, + PatternAmbientModule, + PostfixUnaryExpression, + PrefixUnaryExpression, + PrivateIdentifier, + PropertyAccessChain, + PropertyAccessEntityNameExpression, + PropertyAccessExpression, + PropertyDeclaration, + PropertySignature, + QualifiedName, + ReturnStatement, + ScriptTarget, + SetAccessorDeclaration, + ShorthandPropertyAssignment, + SignatureDeclaration, + SourceFile, + SpreadElement, + Statement, + StringLiteral, + SuperExpression, + SwitchStatement, + Symbol, + SymbolFlags, + SymbolTable, + SyntaxKind, + TextRange, + ThisExpression, + ThrowStatement, + TryStatement, + TypeLiteralNode, + TypeOfExpression, + TypeParameterDeclaration, + VariableDeclaration, + WhileStatement, + WithStatement, +} from "./types"; +import { + addRelatedInfo, + createDiagnosticForNodeInSourceFile, + createFileDiagnostic, + createSymbolTable, + declarationNameToString, + exportAssignmentIsAlias, getAssignedExpandoInitializer, getAssignmentDeclarationKind, getAssignmentDeclarationPropertyAccessKind, - getCombinedModifierFlags, - getCombinedNodeFlags, getContainingClass, getEffectiveContainerForJSDocTemplateTag, getElementOrPropertyAccessName, @@ -93,12 +218,9 @@ import { getExpandoInitializer, getHostSignatureFromJSDoc, getImmediatelyInvokedFunctionExpression, - getJSDocTypeTag, getLeftmostAccessExpression, - getNameOfDeclaration, getNameOrArgument, getNodeId, - getRangesWhere, getRightMostAssignedExpression, getSourceFileOfNode, getSourceTextOfNodeFromSourceFile, @@ -108,213 +230,103 @@ import { getTextOfIdentifierOrLiteral, getThisContainer, getTokenPosOfNode, - HasContainerFlags, hasDynamicName, - HasFlowNode, - hasJSDocNodes, - HasLocals, hasSyntacticModifier, - Identifier, - identifierToKeywordKind, - idText, - IfStatement, - ImportClause, - InternalSymbolName, isAliasableExpression, isAmbientModule, isAssignmentExpression, isAssignmentOperator, isAssignmentTarget, isAsyncFunction, - isAutoAccessorPropertyDeclaration, - isBinaryExpression, isBindableObjectDefinePropertyCall, isBindableStaticAccessExpression, isBindableStaticNameExpression, - isBindingPattern, - isBlock, isBlockOrCatchScoped, - IsBlockScopedContainer, - isCallExpression, - isClassStaticBlockDeclaration, - isConditionalTypeNode, - IsContainer, - isDeclaration, - isDeclarationStatement, isDestructuringAssignment, isDottedName, - isElementAccessExpression, isEmptyObjectLiteral, isEntityNameExpression, isEnumConst, - isEnumDeclaration, - isExportAssignment, - isExportDeclaration, isExportsIdentifier, - isExportSpecifier, - isExpression, - isExpressionOfOptionalChainRoot, isExternalModule, isExternalOrCommonJsModule, - isForInOrOfStatement, - isFunctionDeclaration, - isFunctionLike, - isFunctionLikeDeclaration, - isFunctionLikeOrClassStaticBlockDeclaration, isFunctionSymbol, isGlobalScopeAugmentation, - isIdentifier, isIdentifierName, isInJSFile, isInTopLevelContext, isJSDocConstructSignature, - isJSDocEnumTag, - isJSDocTemplateTag, isJSDocTypeAlias, isJsonSourceFile, - isJsxNamespacedName, - isLeftHandSideExpression, isLogicalOrCoalescingAssignmentExpression, isLogicalOrCoalescingAssignmentOperator, isLogicalOrCoalescingBinaryExpression, isLogicalOrCoalescingBinaryOperator, isModuleAugmentationExternal, - isModuleBlock, - isModuleDeclaration, isModuleExportsAccessExpression, - isNamedDeclaration, - isNamespaceExport, - isNonNullExpression, - isNullishCoalesce, - isObjectLiteralExpression, isObjectLiteralMethod, isObjectLiteralOrClassExpressionMethodOrAccessor, - isOmittedExpression, - isOptionalChain, - isOptionalChainRoot, - isOutermostOptionalChain, isParameterDeclaration, - isParameterPropertyDeclaration, - isParenthesizedExpression, isPartOfTypeQuery, - isPrefixUnaryExpression, - isPrivateIdentifier, isPrologueDirective, isPropertyAccessEntityNameExpression, - isPropertyAccessExpression, isPropertyNameLiteral, isPrototypeAccess, isPushOrUnshiftIdentifier, isRequireCall, - isShorthandPropertyAssignment, isSignedNumericLiteral, - isSourceFile, isSpecialPropertyDeclaration, - isStatement, - isStatementButNotDeclaration, isStatic, - isString, - isStringLiteralLike, isStringOrNumericLiteralLike, isThisInitializedDeclaration, - isTypeAliasDeclaration, - isTypeOfExpression, - isVariableDeclaration, isVariableDeclarationInitializedToBareOrAccessedRequire, - isVariableStatement, - JSDocCallbackTag, - JSDocClassTag, - JSDocEnumTag, - JSDocFunctionType, - JSDocOverloadTag, - JSDocParameterTag, - JSDocPropertyLikeTag, - JSDocSignature, - JSDocTypedefTag, - JSDocTypeLiteral, - JsxAttribute, - JsxAttributes, - LabeledStatement, - length, - LiteralLikeElementAccessExpression, - MappedTypeNode, - MetaProperty, - MethodDeclaration, - ModifierFlags, - ModuleBlock, - ModuleDeclaration, - Mutable, - NamespaceExportDeclaration, - Node, - NodeArray, - NodeFlags, - nodeHasName, nodeIsMissing, nodeIsPresent, - NonNullChain, - NonNullExpression, - objectAllocator, - ObjectLiteralExpression, - OptionalChain, - ParameterDeclaration, - ParenthesizedExpression, - Pattern, - PatternAmbientModule, - perfLogger, - PostfixUnaryExpression, - PrefixUnaryExpression, - PrivateIdentifier, - PropertyAccessChain, - PropertyAccessEntityNameExpression, - PropertyAccessExpression, - PropertyDeclaration, - PropertySignature, - QualifiedName, - removeFileExtension, - ReturnStatement, - ScriptTarget, - SetAccessorDeclaration, - setParent, - setParentRecursive, setValueDeclaration, - ShorthandPropertyAssignment, shouldPreserveConstEnums, shouldResolveJsRequire, - SignatureDeclaration, skipParentheses, sliceAfter, - some, - SourceFile, - SpreadElement, - Statement, - StringLiteral, - SuperExpression, - SwitchStatement, - Symbol, - SymbolFlags, - symbolName, - SymbolTable, - SyntaxKind, - TextRange, - ThisExpression, - ThrowStatement, - tokenToString, - tracing, - TracingNode, - tryCast, tryParsePattern, - TryStatement, - TypeLiteralNode, - TypeOfExpression, - TypeParameterDeclaration, - unescapeLeadingUnderscores, unreachableCodeIsError, unusedLabelIsError, - VariableDeclaration, - WhileStatement, - WithStatement, -} from "./_namespaces/ts"; -import * as performance from "./_namespaces/ts.performance"; +} from "./utilities"; +import { + canHaveLocals, + canHaveSymbol, + escapeLeadingUnderscores, + findAncestor, + getCombinedModifierFlags, + getCombinedNodeFlags, + getJSDocTypeTag, + getNameOfDeclaration, + hasJSDocNodes, + identifierToKeywordKind, + idText, + isAutoAccessorPropertyDeclaration, + isBindingPattern, + isDeclaration, + isDeclarationStatement, + isExpression, + isExpressionOfOptionalChainRoot, + isForInOrOfStatement, + isFunctionLike, + isFunctionLikeDeclaration, + isFunctionLikeOrClassStaticBlockDeclaration, + isLeftHandSideExpression, + isNamedDeclaration, + isNullishCoalesce, + isOptionalChain, + isOptionalChainRoot, + isOutermostOptionalChain, + isParameterPropertyDeclaration, + isStatement, + isStatementButNotDeclaration, + isStringLiteralLike, + nodeHasName, + symbolName, + unescapeLeadingUnderscores, +} from "./utilitiesPublic"; /** @internal */ export const enum ModuleInstanceState { diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 80724963973e3..8f799e91a8762 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -1,86 +1,102 @@ import { - addRange, AffectedFileResult, - arrayFrom, - arrayToMap, BuilderProgram, BuilderProgramHost, - BuilderState, + EmitAndSemanticDiagnosticsBuilderProgram, + HostForComputeHash, + SemanticDiagnosticsBuilderProgram, +} from "./builderPublic"; +import { BuilderState } from "./builderState"; +import { + convertToOptionsWithAbsolutePaths, + getOptionsNameMap, +} from "./commandLineParser"; +import { + addRange, + arrayFrom, + arrayToMap, + compareStringsCaseSensitive, + compareValues, + concatenate, + createGetCanonicalFileName, + emptyArray, + forEach, + getOwnKeys, + isArray, + isNumber, + isString, + map, + mapDefined, + maybeBind, + noop, + notImplemented, + returnFalse, + returnUndefined, + some, + tryAddToSet, +} from "./core"; +import { ReadonlyCollection } from "./corePublic"; +import * as Debug from "./debug"; +import { + createBuildInfo, + getTsBuildInfoEmitOutputFilePath, +} from "./emitter"; +import { isDeclarationFileName } from "./parser"; +import { + ensurePathIsNonModuleName, + getDirectoryPath, + getNormalizedAbsolutePath, + getRelativePathFromDirectory, + toPath, +} from "./path"; +import { + createProgram, + emitSkippedWithNoDiagnostics, + filterSemanticDiagnostics, + handleNoEmitOptions, +} from "./program"; +import { + compilerOptionsAffectDeclarationPath, + compilerOptionsAffectEmit, + compilerOptionsAffectSemanticDiagnostics, +} from "./programUtilities"; +import { generateDjb2Hash } from "./sys"; +import { BuildInfo, BundleBuildInfo, CancellationToken, CommandLineOption, - compareStringsCaseSensitive, - compareValues, CompilerHost, CompilerOptions, - compilerOptionsAffectDeclarationPath, - compilerOptionsAffectEmit, - compilerOptionsAffectSemanticDiagnostics, CompilerOptionsValue, - concatenate, - convertToOptionsWithAbsolutePaths, - createBuildInfo, - createGetCanonicalFileName, - createProgram, CustomTransformers, - Debug, Diagnostic, DiagnosticCategory, DiagnosticMessageChain, DiagnosticRelatedInformation, DiagnosticWithLocation, - EmitAndSemanticDiagnosticsBuilderProgram, EmitOnly, EmitResult, - emitSkippedWithNoDiagnostics, - emptyArray, - ensurePathIsNonModuleName, FileIncludeKind, - filterSemanticDiagnostics, - forEach, + Path, + Program, + ProjectReference, + SourceFile, + SourceMapEmitResult, + WriteFileCallback, + WriteFileCallbackData, +} from "./types"; +import { forEachEntry, forEachKey, - generateDjb2Hash, - getDirectoryPath, getEmitDeclarations, getIsolatedModules, - getNormalizedAbsolutePath, - getOptionsNameMap, - getOwnKeys, - getRelativePathFromDirectory, - getTsBuildInfoEmitOutputFilePath, - handleNoEmitOptions, - HostForComputeHash, - isArray, - isDeclarationFileName, isJsonSourceFile, - isNumber, - isString, - map, - mapDefined, - maybeBind, - noop, - notImplemented, outFile, - Path, - Program, - ProjectReference, - ReadBuildProgramHost, - ReadonlyCollection, - returnFalse, - returnUndefined, - SemanticDiagnosticsBuilderProgram, skipTypeChecking, - some, - SourceFile, sourceFileMayBeEmitted, - SourceMapEmitResult, - toPath, - tryAddToSet, - WriteFileCallback, - WriteFileCallbackData, -} from "./_namespaces/ts"; +} from "./utilities"; +import { ReadBuildProgramHost } from "./watchPublic"; /** @internal */ export interface ReusableDiagnostic extends ReusableDiagnosticRelatedInformation { diff --git a/src/compiler/builderPublic.ts b/src/compiler/builderPublic.ts index 4cfcb731fb61c..2768a3aab264a 100644 --- a/src/compiler/builderPublic.ts +++ b/src/compiler/builderPublic.ts @@ -1,22 +1,24 @@ import { BuilderProgramKind, + createBuilderProgram, + createRedirectedBuilderProgram, + getBuilderCreationParameters, + ReusableBuilderProgramState, + SavedBuildProgramEmitState, +} from "./builder"; +import { CancellationToken, CompilerHost, CompilerOptions, - createBuilderProgram, - createRedirectedBuilderProgram, CustomTransformers, Diagnostic, DiagnosticWithLocation, EmitResult, - getBuilderCreationParameters, Program, ProjectReference, - ReusableBuilderProgramState, - SavedBuildProgramEmitState, SourceFile, WriteFileCallback, -} from "./_namespaces/ts"; +} from "./types"; export type AffectedFileResult = { result: T; affected: SourceFile | Program; } | undefined; diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 5424336399c4e..e07fa4bec9148 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -1,39 +1,47 @@ +import { computeSignatureWithDiagnostics } from "./builder"; +import { HostForComputeHash } from "./builderPublic"; import { - arrayFrom, - CancellationToken, - computeSignatureWithDiagnostics, - CustomTransformers, - Debug, EmitOutput, + OutputFile, +} from "./builderStatePublic"; +import { + arrayFrom, emptyArray, - ExportedModulesFromDeclarationEmit, GetCanonicalFileName, - getDirectoryPath, - getIsolatedModules, - getSourceFileOfNode, - HostForComputeHash, - isDeclarationFileName, - isExternalOrCommonJsModule, - isGlobalScopeAugmentation, - isJsonSourceFile, - isModuleWithStringLiteralName, - isStringLiteral, mapDefined, mapDefinedIterator, + some, +} from "./core"; +import * as Debug from "./debug"; +import { isStringLiteral } from "./factory/nodeTests"; +import { isDeclarationFileName } from "./parser"; +import { + getDirectoryPath, + toPath, +} from "./path"; +import { + CancellationToken, + CustomTransformers, + ExportedModulesFromDeclarationEmit, ModuleDeclaration, ModuleKind, - outFile, - OutputFile, Path, Program, ResolutionMode, - some, SourceFile, StringLiteralLike, Symbol, - toPath, TypeChecker, -} from "./_namespaces/ts"; +} from "./types"; +import { + getIsolatedModules, + getSourceFileOfNode, + isExternalOrCommonJsModule, + isGlobalScopeAugmentation, + isJsonSourceFile, + isModuleWithStringLiteralName, + outFile, +} from "./utilities"; /** @internal */ export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, diff --git a/src/compiler/builderStatePublic.ts b/src/compiler/builderStatePublic.ts index 17ffc222e30d1..677b62d2b01fc 100644 --- a/src/compiler/builderStatePublic.ts +++ b/src/compiler/builderStatePublic.ts @@ -1,7 +1,7 @@ import { Diagnostic, WriteFileCallbackData, -} from "./_namespaces/ts"; +} from "./types"; export interface EmitOutput { outputFiles: OutputFile[]; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1266af4184020..3b964ddfa7ac5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1,192 +1,38 @@ import { - __String, - AccessExpression, - AccessFlags, - AccessorDeclaration, + bindSourceFile, + getModuleInstanceState, + ModuleInstanceState, +} from "./binder"; +import { addRange, - addRelatedInfo, - addSyntheticLeadingComment, - AliasDeclarationNode, - AllAccessorDeclarations, - AmbientModuleDeclaration, and, - AnonymousType, - AnyImportOrReExport, - AnyImportSyntax, append, appendIfUnique, - ArrayBindingPattern, arrayFrom, - arrayIsHomogeneous, - ArrayLiteralExpression, arrayOf, arraysEqual, arrayToMultiMap, - ArrayTypeNode, - ArrowFunction, - AssertionExpression, - AssignmentDeclarationKind, - AssignmentKind, - AssignmentPattern, - AwaitExpression, - BaseType, - BigIntLiteral, - BigIntLiteralType, - BinaryExpression, - BinaryOperatorToken, binarySearch, - BindableObjectDefinePropertyCall, - BindableStaticNameExpression, - BindingElement, - BindingElementGrandparent, - BindingName, - BindingPattern, - bindSourceFile, - Block, - BreakOrContinueStatement, - CallChain, - CallExpression, - CallLikeExpression, - CallSignatureDeclaration, - CancellationToken, - canHaveDecorators, - canHaveExportModifier, - canHaveFlowNode, - canHaveIllegalDecorators, - canHaveIllegalModifiers, - canHaveJSDoc, - canHaveLocals, - canHaveModifiers, - canHaveSymbol, - canUsePropertyAccess, cartesianProduct, - CaseBlock, - CaseClause, - CaseOrDefaultClause, cast, - chainDiagnosticMessages, - CharacterCodes, - CheckFlags, - ClassDeclaration, - ClassElement, - classElementOrClassElementParameterIsDecorated, - ClassExpression, - ClassLikeDeclaration, - classOrConstructorParameterIsDecorated, - ClassStaticBlockDeclaration, clear, - combinePaths, - compareDiagnostics, - comparePaths, compareValues, - Comparison, - CompilerOptions, - ComputedPropertyName, concatenate, - concatenateDiagnosticMessageChains, - ConditionalExpression, - ConditionalRoot, - ConditionalType, - ConditionalTypeNode, - ConstructorDeclaration, - ConstructorTypeNode, - ConstructSignatureDeclaration, contains, - containsParseError, - ContextFlags, - copyEntries, countWhere, - createBinaryExpressionTrampoline, - createCompilerDiagnostic, - createDiagnosticCollection, - createDiagnosticForFileFromMessageChain, - createDiagnosticForNode, - createDiagnosticForNodeArray, - createDiagnosticForNodeArrayFromMessageChain, - createDiagnosticForNodeFromMessageChain, - createDiagnosticMessageChainFromDiagnostic, - createEmptyExports, - createFileDiagnostic, createGetCanonicalFileName, - createGetSymbolWalker, - createModeAwareCacheKey, createMultiMap, - createPrinterWithDefaults, - createPrinterWithRemoveComments, - createPrinterWithRemoveCommentsNeverAsciiEscape, - createPrinterWithRemoveCommentsOmitTrailingSemicolon, - createPropertyNameNodeForIdentifierOrLiteral, - createSymbolTable, - createTextWriter, - Debug, - Declaration, - DeclarationName, - declarationNameToString, - DeclarationStatement, - DeclarationWithTypeParameterChildren, - DeclarationWithTypeParameters, - Decorator, deduplicate, - DefaultClause, - defaultMaximumTruncationLength, - DeferredTypeReference, - DeleteExpression, - Diagnostic, - DiagnosticAndArguments, - DiagnosticArguments, - DiagnosticCategory, - DiagnosticMessage, - DiagnosticMessageChain, - DiagnosticRelatedInformation, - Diagnostics, - DiagnosticWithLocation, - DoStatement, - DynamicNamedDeclaration, - ElementAccessChain, - ElementAccessExpression, - ElementFlags, - EmitFlags, - EmitHint, - emitModuleKindIsNonNodeESM, - EmitResolver, - EmitTextWriter, emptyArray, endsWith, - EntityName, - EntityNameExpression, - EntityNameOrEntityNameExpression, - entityNameToString, - EnumDeclaration, - EnumMember, - EnumType, equateValues, - escapeLeadingUnderscores, - escapeString, every, - EvolvingArrayType, - ExclamationToken, - ExportAssignment, - exportAssignmentIsAlias, - ExportDeclaration, - ExportSpecifier, - Expression, - expressionResultIsUnused, - ExpressionStatement, - ExpressionWithTypeArguments, - Extension, - ExternalEmitHelpers, - externalHelpersModuleNameText, - factory, - fileExtensionIs, - fileExtensionIsOneOf, filter, find, - findAncestor, findBestPatternMatch, findIndex, findLast, findLastIndex, - findUseStrictPrologue, first, firstDefined, firstIterator, @@ -194,554 +40,422 @@ import { firstOrUndefinedIterator, flatMap, flatten, - FlowArrayMutation, - FlowAssignment, - FlowCall, - FlowCondition, - FlowFlags, - FlowLabel, - FlowNode, - FlowReduceLabel, - FlowStart, - FlowSwitchClause, - FlowType, forEach, - forEachChild, - forEachChildRecursively, - forEachEnclosingBlockScopeContainer, - forEachEntry, - forEachImportClauseDeclaration, - forEachKey, - forEachReturnStatement, - forEachYieldExpression, - ForInOrOfStatement, - ForInStatement, - formatMessage, - ForOfStatement, - ForStatement, - FreshableIntrinsicType, - FreshableType, - FreshObjectLiteralType, - FunctionDeclaration, - FunctionExpression, - FunctionFlags, - FunctionLikeDeclaration, - FunctionOrConstructorTypeNode, - FunctionTypeNode, - GenericType, - GetAccessorDeclaration, - getAliasDeclarationFromName, - getAllAccessorDeclarations, - getAllJSDocTags, - getAllowSyntheticDefaultImports, - getAncestor, - getAssignedExpandoInitializer, - getAssignmentDeclarationKind, - getAssignmentDeclarationPropertyAccessKind, - getAssignmentTargetKind, - getCheckFlags, - getClassExtendsHeritageElement, - getClassLikeDeclarationOfSymbol, - getCombinedLocalAndExportSymbolFlags, - getCombinedModifierFlags, - getCombinedNodeFlags, - getContainingClass, - getContainingClassStaticBlock, - getContainingFunction, - getContainingFunctionOrClassStaticBlock, - getDeclarationModifierFlagsFromSymbol, - getDeclarationOfKind, - getDeclarationsOfKind, - getDeclaredExpandoInitializer, - getDecorators, - getDirectoryPath, - getEffectiveBaseTypeNode, - getEffectiveConstraintOfTypeParameter, - getEffectiveContainerForJSDocTemplateTag, - getEffectiveImplementsTypeNodes, - getEffectiveInitializer, - getEffectiveJSDocHost, - getEffectiveModifierFlags, - getEffectiveReturnTypeNode, - getEffectiveSetAccessorTypeAnnotationNode, - getEffectiveTypeAnnotationNode, - getEffectiveTypeParameterDeclarations, - getElementOrPropertyAccessName, - getEmitDeclarations, - getEmitModuleKind, - getEmitModuleResolutionKind, - getEmitScriptTarget, - getEnclosingBlockScopeContainer, - getEntityNameFromTypeNode, - getErrorSpanForNode, - getEscapedTextOfIdentifierOrLiteral, - getEscapedTextOfJsxAttributeName, - getEscapedTextOfJsxNamespacedName, - getESModuleInterop, - getExpandoInitializer, - getExportAssignmentExpression, - getExternalModuleImportEqualsDeclarationExpression, - getExternalModuleName, - getExternalModuleRequireArgument, - getFirstConstructorWithBody, - getFirstIdentifier, - getFunctionFlags, - getHostSignatureFromJSDoc, - getIdentifierGeneratedImportReference, - getIdentifierTypeArguments, - getImmediatelyInvokedFunctionExpression, - getInitializerOfBinaryExpression, - getInterfaceBaseTypeNodes, - getInvokedExpression, - getIsolatedModules, - getJSDocClassTag, - getJSDocDeprecatedTag, - getJSDocEnumTag, - getJSDocHost, - getJSDocParameterTags, - getJSDocRoot, - getJSDocSatisfiesExpressionType, - getJSDocTags, - getJSDocThisTag, - getJSDocType, - getJSDocTypeAssertionType, - getJSDocTypeParameterDeclarations, - getJSDocTypeTag, - getJSXImplicitImportBase, - getJSXRuntimeImport, - getJSXTransformEnabled, - getLeftmostAccessExpression, - getLineAndCharacterOfPosition, - getLocalSymbolForExportDefault, - getMembersOfDeclaration, - getModeForUsageLocation, - getModifiers, - getModuleInstanceState, - getNameFromIndexInfo, - getNameOfDeclaration, - getNameOfExpando, - getNamespaceDeclarationNode, - getNewTargetContainer, - getNonAugmentationDeclaration, - getNormalizedAbsolutePath, - getObjectFlags, - getOriginalNode, getOrUpdate, - getParameterSymbolFromJSDoc, - getParseTreeNode, - getPropertyAssignmentAliasLikeExpression, - getPropertyNameForPropertyNameNode, - getResolutionDiagnostic, - getResolutionModeOverrideForClause, - getResolvedExternalModuleName, - getResolvedModule, - getResolveJsonModule, - getRestParameterElementType, - getRootDeclaration, - getScriptTargetFeatures, - getSelectedEffectiveModifierFlags, - getSemanticJsxChildren, - getSetAccessorValueParameter, - getSingleVariableOfVariableStatement, - getSourceFileOfModule, - getSourceFileOfNode, - getSpanOfTokenAtPosition, getSpellingSuggestion, - getStrictOptionValue, - getSuperContainer, - getSymbolNameForPrivateIdentifier, - getTextOfIdentifierOrLiteral, - getTextOfJSDocComment, - getTextOfJsxAttributeName, - getTextOfNode, - getTextOfPropertyName, - getThisContainer, - getThisParameter, - getTrailingSemicolonDeferringWriter, - getTypeParameterFromJsDoc, - getTypesPackageName, - getUseDefineForClassFields, group, - hasAbstractModifier, - hasAccessorModifier, - hasAmbientModifier, - hasContextSensitiveParameters, - HasDecorators, - hasDecorators, - hasDynamicName, - hasEffectiveModifier, - hasEffectiveModifiers, - hasEffectiveReadonlyModifier, - HasExpressionInitializer, - hasExtension, - HasIllegalDecorators, - HasIllegalModifiers, - HasInitializer, - hasInitializer, - hasJSDocNodes, - hasJSDocParameterTags, - hasJsonModuleEmitEnabled, - HasLocals, - HasModifiers, - hasOnlyExpressionInitializer, - hasOverrideModifier, - hasPossibleExternalModuleReference, - hasQuestionToken, - hasRestParameter, - hasScopeMarker, - hasStaticModifier, - hasSyntacticModifier, - hasSyntacticModifiers, - hasType, - HeritageClause, - Identifier, - identifierToKeywordKind, - IdentifierTypePredicate, - idText, - IfStatement, - ImportCall, - ImportClause, - ImportDeclaration, - ImportEqualsDeclaration, - ImportOrExportSpecifier, - ImportsNotUsedAsValues, - ImportSpecifier, - ImportTypeAssertionContainer, - ImportTypeNode, - IndexedAccessType, - IndexedAccessTypeNode, - IndexFlags, - IndexInfo, - IndexKind, - indexOfNode, - IndexSignatureDeclaration, - IndexType, indicesOf, - InferenceContext, - InferenceFlags, - InferenceInfo, - InferencePriority, - InferTypeNode, - InstantiableType, - InstantiationExpressionType, - InterfaceDeclaration, - InterfaceType, - InterfaceTypeWithDeclaredMembers, - InternalSymbolName, - IntersectionType, - IntersectionTypeNode, - intrinsicTagNameToString, - IntrinsicType, - introducesArgumentsExoticObject, - isAccessExpression, - isAccessor, - isAliasableExpression, - isAmbientModule, isArray, + isLineBreak, + isString, + last, + lastOrUndefined, + length, + map, + mapDefined, + maybeBind, + memoize, + not, + or, + orderedRemoveItemAt, + pushIfUnique, + rangeEquals, + reduceLeft, + relativeComplement, + removePrefix, + replaceElement, + sameMap, + singleElementArray, + some, + startsWith, + stringContains, + tryAddToSet, + tryCast, +} from "./core"; +import { Comparison } from "./corePublic"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { createPrinterWithDefaults, createPrinterWithRemoveComments, createPrinterWithRemoveCommentsNeverAsciiEscape, createPrinterWithRemoveCommentsOmitTrailingSemicolon } from "./emitter"; +import { + removeExtension, + resolutionExtensionIsTSOrJson, + tryExtractTSExtension, + tryGetExtensionFromPath, +} from "./extension"; +import { + createBinaryExpressionTrampoline, +} from "./factory/binaryExpressionStateMachine"; +import { + addSyntheticLeadingComment, + getIdentifierGeneratedImportReference, + getIdentifierTypeArguments, + setCommentRange, + setEmitFlags, + setIdentifierTypeArguments, + setSyntheticLeadingComments, +} from "./factory/emitNode"; +import { factory } from "./factory/nodeFactory"; +import { isArrayBindingPattern, isArrayLiteralExpression, isArrowFunction, - isAssertionExpression, - isAssignmentDeclaration, - isAssignmentExpression, - isAssignmentOperator, - isAssignmentPattern, - isAssignmentTarget, - isAsyncFunction, - isAutoAccessorPropertyDeclaration, isAwaitExpression, isBinaryExpression, - isBindableObjectDefinePropertyCall, - isBindableStaticElementAccessExpression, - isBindableStaticNameExpression, isBindingElement, - isBindingElementOfBareOrAccessedRequire, - isBindingPattern, isBlock, - isBlockOrCatchScoped, - isBlockScopedContainerTopLevel, - isBooleanLiteral, - isCallChain, isCallExpression, - isCallLikeExpression, - isCallOrNewExpression, isCallSignatureDeclaration, isCatchClause, - isCatchClauseVariableDeclarationOrBindingElement, - isCheckJsEnabledForFile, isClassDeclaration, - isClassElement, isClassExpression, - isClassLike, isClassStaticBlockDeclaration, - isCommaSequence, - isCommonJsExportedExpression, - isCommonJsExportPropertyAssignment, - isCompoundAssignment, - isComputedNonLiteralName, isComputedPropertyName, isConstructorDeclaration, isConstructorTypeNode, isConstructSignatureDeclaration, - isConstTypeReference, - isDeclaration, - isDeclarationFileName, - isDeclarationName, - isDeclarationReadonly, isDecorator, - isDefaultedExpandoInitializer, - isDeleteTarget, - isDottedName, - isDynamicName, - isEffectiveExternalModule, isElementAccessExpression, - isEntityName, - isEntityNameExpression, - isEnumConst, isEnumDeclaration, isEnumMember, - isExclusivelyTypeOnlyImportOrExport, isExportAssignment, isExportDeclaration, - isExportsIdentifier, isExportSpecifier, - isExpression, - isExpressionNode, - isExpressionOfOptionalChainRoot, isExpressionStatement, isExpressionWithTypeArguments, - isExpressionWithTypeArgumentsInClassExtendsClause, - isExternalModule, - isExternalModuleAugmentation, - isExternalModuleImportEqualsDeclaration, - isExternalModuleIndicator, - isExternalModuleNameRelative, isExternalModuleReference, - isExternalOrCommonJsModule, - isForInOrOfStatement, isForInStatement, isForOfStatement, isForStatement, isFunctionDeclaration, isFunctionExpression, - isFunctionExpressionOrArrowFunction, - isFunctionLike, - isFunctionLikeDeclaration, - isFunctionLikeOrClassStaticBlockDeclaration, - isFunctionOrModuleBlock, isFunctionTypeNode, - isGeneratedIdentifier, - isGetAccessor, isGetAccessorDeclaration, - isGetOrSetAccessorDeclaration, - isGlobalScopeAugmentation, isHeritageClause, isIdentifier, - isIdentifierText, - isIdentifierTypePredicate, - isIdentifierTypeReference, isIfStatement, - isImportCall, isImportClause, isImportDeclaration, isImportEqualsDeclaration, isImportKeyword, - isImportOrExportSpecifier, isImportSpecifier, isImportTypeNode, isIndexedAccessTypeNode, - isInExpressionContext, - isInfinityOrNaNString, - isInitializedProperty, - isInJSDoc, - isInJSFile, - isInJsonFile, isInterfaceDeclaration, - isInternalModuleImportEqualsDeclaration, - isInTopLevelContext, - isIntrinsicJsxName, - isIterationStatement, isJSDocAllType, isJSDocAugmentsTag, isJSDocCallbackTag, - isJSDocConstructSignature, isJSDocFunctionType, - isJSDocIndexSignature, - isJSDocLinkLike, isJSDocMemberName, isJSDocNameReference, - isJSDocNode, isJSDocNonNullableType, isJSDocNullableType, - isJSDocOptionalParameter, isJSDocOptionalType, isJSDocOverloadTag, isJSDocParameterTag, - isJSDocPropertyLikeTag, isJSDocPropertyTag, isJSDocReturnTag, - isJSDocSatisfiesExpression, isJSDocSatisfiesTag, isJSDocSignature, isJSDocTemplateTag, - isJSDocTypeAlias, - isJSDocTypeAssertion, isJSDocTypedefTag, isJSDocTypeExpression, isJSDocTypeLiteral, isJSDocTypeTag, isJSDocUnknownType, isJSDocVariadicType, - isJsonSourceFile, isJsxAttribute, - isJsxAttributeLike, isJsxAttributes, isJsxElement, isJsxNamespacedName, isJsxOpeningElement, isJsxOpeningFragment, - isJsxOpeningLikeElement, isJsxSelfClosingElement, isJsxSpreadAttribute, - isJSXTagName, - isKnownSymbol, - isLateVisibilityPaintedStatement, - isLeftHandSideExpression, - isLet, - isLineBreak, - isLiteralComputedPropertyDeclarationName, - isLiteralExpression, - isLiteralExpressionOfObject, - isLiteralImportTypeNode, isLiteralTypeNode, - isLogicalOrCoalescingBinaryExpression, - isLogicalOrCoalescingBinaryOperator, isMetaProperty, isMethodDeclaration, isMethodSignature, - isModifier, isModuleBlock, isModuleDeclaration, - isModuleExportsAccessExpression, - isModuleIdentifier, - isModuleOrEnumDeclaration, - isModuleWithStringLiteralName, - isNamedDeclaration, - isNamedEvaluationSource, isNamedExports, isNamedTupleMember, isNamespaceExport, isNamespaceExportDeclaration, - isNamespaceReexportDeclaration, isNewExpression, - isNightly, - isNodeDescendantOf, - isNonNullAccess, - isNullishCoalesce, isNumericLiteral, - isNumericLiteralName, isObjectBindingPattern, - isObjectLiteralElementLike, isObjectLiteralExpression, - isObjectLiteralMethod, - isObjectLiteralOrClassExpressionMethodOrAccessor, isOmittedExpression, - isOptionalChain, - isOptionalChainRoot, - isOptionalDeclaration, - isOptionalJSDocPropertyLikeTag, isOptionalTypeNode, - isOutermostOptionalChain, isParameter, - isParameterDeclaration, - isParameterOrCatchClauseVariable, - isParameterPropertyDeclaration, isParenthesizedExpression, isParenthesizedTypeNode, - isPartOfTypeNode, - isPartOfTypeQuery, - isPlainJsFile, isPrefixUnaryExpression, isPrivateIdentifier, - isPrivateIdentifierClassElementDeclaration, - isPrivateIdentifierPropertyAccessExpression, - isPropertyAccessEntityNameExpression, isPropertyAccessExpression, - isPropertyAccessOrQualifiedNameOrImportTypeNode, isPropertyAssignment, isPropertyDeclaration, - isPropertyName, - isPropertyNameLiteral, isPropertySignature, - isPrototypeAccess, - isPrototypePropertyAssignment, - isPushOrUnshiftIdentifier, isQualifiedName, - isRequireCall, - isRestParameter, isRestTypeNode, - isRightSideOfQualifiedNameOrPropertyAccess, - isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName, - isSameEntityName, - isSetAccessor, isSetAccessorDeclaration, - isShorthandAmbientModuleSymbol, isShorthandPropertyAssignment, - isSingleOrDoubleQuote, isSourceFile, - isSourceFileJS, isSpreadAssignment, isSpreadElement, - isStatement, - isStatementWithLocals, - isStatic, - isString, - isStringANonContextualKeyword, isStringLiteral, - isStringLiteralLike, - isStringOrNumericLiteralLike, - isSuperCall, - isSuperProperty, isTaggedTemplateExpression, isTemplateSpan, - isThisContainerOrFunctionBlock, - isThisIdentifier, - isThisInitializedDeclaration, - isThisInitializedObjectBindingExpression, - isThisInTypeQuery, - isThisProperty, - isThisTypeParameter, - isTransientSymbol, isTupleTypeNode, - isTypeAlias, isTypeAliasDeclaration, - isTypeDeclaration, isTypeLiteralNode, - isTypeNode, - isTypeNodeKind, isTypeOfExpression, - isTypeOnlyImportOrExportDeclaration, isTypeOperatorNode, isTypeParameterDeclaration, isTypePredicateNode, isTypeQueryNode, isTypeReferenceNode, - isTypeReferenceType, - isUMDExportSymbol, - isValidBigIntString, - isValidESSymbolDeclaration, - isValidTypeOnlyAliasUseSite, - isValueSignatureDeclaration, - isVarConst, isVariableDeclaration, - isVariableDeclarationInitializedToBareOrAccessedRequire, - isVariableDeclarationInVariableStatement, isVariableDeclarationList, - isVariableLike, - isVariableLikeOrAccessor, isVariableStatement, - isWriteAccess, - isWriteOnlyAccess, +} from "./factory/nodeTests"; +import { + canHaveIllegalModifiers, + createEmptyExports, + createPropertyNameNodeForIdentifierOrLiteral, + getJSDocTypeAssertionType, + isCommaSequence, +} from "./factory/utilities"; +import { + canHaveDecorators, + canHaveModifiers, + setOriginalNode, + setTextRange, +} from "./factory/utilitiesPublic"; +import { + createModeAwareCacheKey, + getTypesPackageName, + mangleScopedPackageName, + nodeModulesPathPart, + shouldAllowImportingTsExtension, +} from "./moduleNameResolver"; +import { getResolvedExternalModuleName } from "./moduleNameResolverUtilities"; +import { + countPathComponents, + getModuleSpecifiers, +} from "./moduleSpecifiers"; +import { objectAllocator } from "./objectAllocator"; +import { + forEachChild, + forEachChildRecursively, + isDeclarationFileName, + parseIsolatedEntityName, + parseNodeFactory, +} from "./parser"; +import { + containsParseError, + forEachReturnStatement, + forEachYieldExpression, + setParent, +} from "./parserUtilities"; +import { + combinePaths, + comparePaths, + fileExtensionIs, + fileExtensionIsOneOf, + getDirectoryPath, + getNormalizedAbsolutePath, + hasExtension, + pathIsRelative, +} from "./path"; +import * as performance from "./performance"; +import { + getModeForUsageLocation, + getResolutionDiagnostic, + getResolutionModeOverrideForClause, + isExclusivelyTypeOnlyImportOrExport, + resolveTripleslashReference, +} from "./program"; +import { + getLineAndCharacterOfPosition, + isIdentifierText, + skipTrivia, + tokenToString, +} from "./scanner"; +import { parsePseudoBigInt } from "./scannerUtilities"; +import { createGetSymbolWalker } from "./symbolWalker"; +import { + tracing, + TracingNode, +} from "./tracing"; +import { nullTransformationContext } from "./transformer"; +import { isCompoundAssignment, isInitializedProperty } from "./transformers/utilities"; +import { + __String, + AccessExpression, + AccessFlags, + AccessorDeclaration, + AliasDeclarationNode, + AllAccessorDeclarations, + AmbientModuleDeclaration, + AnonymousType, + AnyImportOrReExport, + AnyImportSyntax, + ArrayBindingPattern, + ArrayLiteralExpression, + ArrayTypeNode, + ArrowFunction, + AssertionExpression, + AssignmentDeclarationKind, + AssignmentPattern, + AwaitExpression, + BaseType, + BigIntLiteral, + BigIntLiteralType, + BinaryExpression, + BinaryOperatorToken, + BindableObjectDefinePropertyCall, + BindableStaticNameExpression, + BindingElement, + BindingElementGrandparent, + BindingName, + BindingPattern, + Block, + BreakOrContinueStatement, + CallChain, + CallExpression, + CallLikeExpression, + CallSignatureDeclaration, + CancellationToken, + CaseBlock, + CaseClause, + CaseOrDefaultClause, + CharacterCodes, + CheckFlags, + CheckMode, + ClassDeclaration, + ClassElement, + ClassExpression, + ClassLikeDeclaration, + ClassStaticBlockDeclaration, + CompilerOptions, + ComputedPropertyName, + ConditionalExpression, + ConditionalRoot, + ConditionalType, + ConditionalTypeNode, + ConstructorDeclaration, + ConstructorTypeNode, + ConstructSignatureDeclaration, + ContextFlags, + Declaration, + DeclarationName, + DeclarationStatement, + DeclarationWithTypeParameterChildren, + DeclarationWithTypeParameters, + Decorator, + DefaultClause, + DeferredTypeReference, + DeleteExpression, + Diagnostic, + DiagnosticAndArguments, + DiagnosticArguments, + DiagnosticCategory, + DiagnosticMessage, + DiagnosticMessageChain, + DiagnosticRelatedInformation, + DiagnosticWithLocation, + DoStatement, + DynamicNamedDeclaration, + ElementAccessChain, + ElementAccessExpression, + ElementFlags, + EmitFlags, + EmitHint, + EmitResolver, + EmitTextWriter, + EntityName, + EntityNameExpression, + EntityNameOrEntityNameExpression, + EnumDeclaration, + EnumMember, + EnumType, + EvolvingArrayType, + ExclamationToken, + ExportAssignment, + ExportDeclaration, + ExportSpecifier, + Expression, + ExpressionStatement, + ExpressionWithTypeArguments, + Extension, + ExternalEmitHelpers, + FlowArrayMutation, + FlowAssignment, + FlowCall, + FlowCondition, + FlowFlags, + FlowLabel, + FlowNode, + FlowReduceLabel, + FlowStart, + FlowSwitchClause, + FlowType, + ForInOrOfStatement, + ForInStatement, + ForOfStatement, + ForStatement, + FreshableIntrinsicType, + FreshableType, + FreshObjectLiteralType, + FunctionDeclaration, + FunctionExpression, + FunctionLikeDeclaration, + FunctionOrConstructorTypeNode, + FunctionTypeNode, + GenericType, + GetAccessorDeclaration, + HasDecorators, + HasExpressionInitializer, + HasIllegalDecorators, + HasIllegalModifiers, + HasInitializer, + HasLocals, + HasModifiers, + HeritageClause, + Identifier, + IdentifierTypePredicate, + IfStatement, + ImportCall, + ImportClause, + ImportDeclaration, + ImportEqualsDeclaration, + ImportOrExportSpecifier, + ImportsNotUsedAsValues, + ImportSpecifier, + ImportTypeAssertionContainer, + ImportTypeNode, + IndexedAccessType, + IndexedAccessTypeNode, + IndexFlags, + IndexInfo, + IndexKind, + IndexSignatureDeclaration, + IndexType, + InferenceContext, + InferenceFlags, + InferenceInfo, + InferencePriority, + InferTypeNode, + InstantiableType, + InstantiationExpressionType, + InterfaceDeclaration, + InterfaceType, + InterfaceTypeWithDeclaredMembers, + InternalSymbolName, + IntersectionType, + IntersectionTypeNode, + IntrinsicType, IterableOrIteratorType, IterationTypes, JSDoc, @@ -794,39 +508,27 @@ import { JsxTagNameExpression, KeywordTypeNode, LabeledStatement, - last, - lastOrUndefined, LateBoundBinaryExpressionDeclaration, LateBoundDeclaration, LateBoundName, LateVisibilityPaintedStatement, LeftHandSideExpression, - length, LiteralExpression, LiteralType, LiteralTypeNode, - mangleScopedPackageName, - map, - mapDefined, MappedSymbol, MappedType, MappedTypeNode, MatchingKeys, - maybeBind, MemberOverrideStatus, - memoize, MetaProperty, MethodDeclaration, MethodSignature, - minAndMax, MinusToken, Modifier, ModifierFlags, - modifiersToFlags, - modifierToFlag, ModuleBlock, ModuleDeclaration, - ModuleInstanceState, ModuleKind, ModuleResolutionKind, ModuleSpecifierResolutionHost, @@ -838,31 +540,18 @@ import { NamespaceExport, NamespaceExportDeclaration, NamespaceImport, - needsScopeMarker, NewExpression, Node, NodeArray, NodeBuilderFlags, - nodeCanBeDecorated, NodeCheckFlags, NodeFlags, - nodeHasName, - nodeIsDecorated, - nodeIsMissing, - nodeIsPresent, - nodeIsSynthesized, NodeLinks, - nodeModulesPathPart, - nodeStartsNewLexicalEnvironment, NodeWithTypeArguments, NonNullChain, NonNullExpression, - not, - noTruncationMaximumTruncationLength, - nullTransformationContext, NumberLiteralType, NumericLiteral, - objectAllocator, ObjectBindingPattern, ObjectFlags, ObjectFlagsType, @@ -871,21 +560,11 @@ import { ObjectType, OptionalChain, OptionalTypeNode, - or, - orderedRemoveItemAt, OuterExpressionKinds, - outFile, ParameterDeclaration, - parameterIsThisKeyword, - ParameterPropertyDeclaration, ParenthesizedExpression, ParenthesizedTypeNode, - parseIsolatedEntityName, - parseNodeFactory, - parsePseudoBigInt, - parseValidBigInt, Path, - pathIsRelative, PatternAmbientModule, PlusToken, PostfixUnaryExpression, @@ -901,72 +580,36 @@ import { PropertyName, PropertySignature, PseudoBigInt, - pseudoBigIntToString, PunctuationSyntaxKind, - pushIfUnique, QualifiedName, QuestionToken, - rangeEquals, - rangeOfNode, - rangeOfTypeParameters, ReadonlyKeyword, - reduceLeft, RelationComparisonResult, - relativeComplement, - removeExtension, - removePrefix, - replaceElement, - resolutionExtensionIsTSOrJson, ResolutionMode, ResolvedModuleFull, ResolvedType, - resolveTripleslashReference, - resolvingEmptyArray, RestTypeNode, ReturnStatement, ReverseMappedSymbol, ReverseMappedType, - sameMap, SatisfiesExpression, - scanTokenAtPosition, ScriptKind, ScriptTarget, SetAccessorDeclaration, - setCommentRange, - setEmitFlags, - setIdentifierTypeArguments, - setNodeFlags, - setOriginalNode, - setParent, - setSyntheticLeadingComments, - setTextRange, - setTextRangePosEnd, - setValueDeclaration, ShorthandPropertyAssignment, - shouldAllowImportingTsExtension, - shouldPreserveConstEnums, - shouldResolveJsRequire, Signature, + SignatureCheckMode, SignatureDeclaration, SignatureFlags, SignatureKind, - singleElementArray, - skipOuterExpressions, - skipParentheses, - skipTrivia, - skipTypeChecking, - some, SourceFile, SpreadAssignment, SpreadElement, - startsWith, Statement, - stringContains, StringLiteral, StringLiteralLike, StringLiteralType, StringMappingType, - stripQuotes, StructuredType, SubstitutionType, SuperCall, @@ -978,7 +621,6 @@ import { SymbolFormatFlags, SymbolId, SymbolLinks, - symbolName, SymbolTable, SymbolTracker, SymbolVisibilityResult, @@ -990,27 +632,13 @@ import { TemplateLiteralType, TemplateLiteralTypeNode, Ternary, - textRangeContainsPositionInclusive, TextSpan, - textSpanContainsPosition, - textSpanEnd, ThisExpression, ThisTypeNode, ThrowStatement, TokenFlags, - tokenToString, - tracing, - TracingNode, TransientSymbol, TransientSymbolLinks, - tryAddToSet, - tryCast, - tryExtractTSExtension, - tryGetClassImplementingOrExtendingExpressionWithTypeArguments, - tryGetExtensionFromPath, - tryGetJSDocSatisfiesTypeNode, - tryGetModuleSpecifierFromDeclaration, - tryGetPropertyAccessOrIdentifierToString, TryStatement, TupleType, TupleTypeNode, @@ -1022,6 +650,7 @@ import { TypeCheckerHost, TypeComparer, TypeElement, + TypeFacts, TypeFlags, TypeFormatFlags, TypeId, @@ -1045,43 +674,454 @@ import { TypeReferenceSerializationKind, TypeReferenceType, TypeVariable, - unescapeLeadingUnderscores, UnionOrIntersectionType, UnionOrIntersectionTypeNode, UnionReduction, UnionType, UnionTypeNode, UniqueESSymbolType, - usingSingleLineStringWriter, VariableDeclaration, VariableDeclarationList, VariableLikeDeclaration, VariableStatement, VarianceFlags, - visitEachChild, - visitNode, - visitNodes, Visitor, VisitResult, VoidExpression, - walkUpBindingElementsAndPatterns, - walkUpOuterExpressions, - walkUpParenthesizedExpressions, - walkUpParenthesizedTypes, - walkUpParenthesizedTypesAndGetParentAndChild, WhileStatement, WideningContext, WithStatement, YieldExpression, -} from "./_namespaces/ts"; -import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers"; -import * as performance from "./_namespaces/ts.performance"; +} from "./types"; +import { + addRelatedInfo, + arrayIsHomogeneous, + AssignmentKind, + canHaveExportModifier, + canHaveFlowNode, + canHaveIllegalDecorators, + canHaveJSDoc, + canUsePropertyAccess, + chainDiagnosticMessages, + classElementOrClassElementParameterIsDecorated, + classOrConstructorParameterIsDecorated, + compareDiagnostics, + concatenateDiagnosticMessageChains, + copyEntries, + createCompilerDiagnostic, + createDiagnosticCollection, + createDiagnosticForFileFromMessageChain, + createDiagnosticForNode, + createDiagnosticForNodeArray, + createDiagnosticForNodeArrayFromMessageChain, + createDiagnosticForNodeFromMessageChain, + createDiagnosticMessageChainFromDiagnostic, + createFileDiagnostic, + createSymbolTable, + createTextWriter, + declarationNameToString, + defaultMaximumTruncationLength, + emitModuleKindIsNonNodeESM, + entityNameToString, + escapeString, + exportAssignmentIsAlias, + expressionResultIsUnused, + externalHelpersModuleNameText, + findUseStrictPrologue, + forEachEnclosingBlockScopeContainer, + forEachEntry, + forEachImportClauseDeclaration, + forEachKey, + formatMessage, + FunctionFlags, + getAliasDeclarationFromName, + getAllAccessorDeclarations, + getAllowSyntheticDefaultImports, + getAncestor, + getAssignedExpandoInitializer, + getAssignmentDeclarationKind, + getAssignmentDeclarationPropertyAccessKind, + getAssignmentTargetKind, + getCheckFlags, + getClassExtendsHeritageElement, + getClassLikeDeclarationOfSymbol, + getCombinedLocalAndExportSymbolFlags, + getContainingClass, + getContainingClassStaticBlock, + getContainingFunction, + getContainingFunctionOrClassStaticBlock, + getDeclarationModifierFlagsFromSymbol, + getDeclarationOfKind, + getDeclarationsOfKind, + getDeclaredExpandoInitializer, + getEffectiveBaseTypeNode, + getEffectiveContainerForJSDocTemplateTag, + getEffectiveImplementsTypeNodes, + getEffectiveInitializer, + getEffectiveJSDocHost, + getEffectiveModifierFlags, + getEffectiveReturnTypeNode, + getEffectiveSetAccessorTypeAnnotationNode, + getEffectiveTypeAnnotationNode, + getElementOrPropertyAccessName, + getEmitDeclarations, + getEmitModuleKind, + getEmitModuleResolutionKind, + getEmitScriptTarget, + getEnclosingBlockScopeContainer, + getEntityNameFromTypeNode, + getErrorSpanForNode, + getEscapedTextOfIdentifierOrLiteral, + getEscapedTextOfJsxAttributeName, + getEscapedTextOfJsxNamespacedName, + getESModuleInterop, + getExpandoInitializer, + getExportAssignmentExpression, + getExternalModuleImportEqualsDeclarationExpression, + getExternalModuleName, + getExternalModuleRequireArgument, + getFirstConstructorWithBody, + getFirstIdentifier, + getFunctionFlags, + getHostSignatureFromJSDoc, + getImmediatelyInvokedFunctionExpression, + getInitializerOfBinaryExpression, + getInterfaceBaseTypeNodes, + getInvokedExpression, + getIsolatedModules, + getJSDocHost, + getJSDocRoot, + getJSDocSatisfiesExpressionType, + getJSDocTypeParameterDeclarations, + getJSXImplicitImportBase, + getJSXRuntimeImport, + getJSXTransformEnabled, + getLeftmostAccessExpression, + getLocalSymbolForExportDefault, + getMembersOfDeclaration, + getNameFromIndexInfo, + getNameOfExpando, + getNamespaceDeclarationNode, + getNewTargetContainer, + getNodeId, + getNonAugmentationDeclaration, + getObjectFlags, + getParameterSymbolFromJSDoc, + getPropertyAssignmentAliasLikeExpression, + getPropertyNameForPropertyNameNode, + getResolvedModule, + getResolveJsonModule, + getRestParameterElementType, + getRootDeclaration, + getScriptTargetFeatures, + getSelectedEffectiveModifierFlags, + getSemanticJsxChildren, + getSetAccessorValueParameter, + getSingleVariableOfVariableStatement, + getSourceFileOfModule, + getSourceFileOfNode, + getSpanOfTokenAtPosition, + getStrictOptionValue, + getSuperContainer, + getSymbolId, + getSymbolNameForPrivateIdentifier, + getTextOfIdentifierOrLiteral, + getTextOfJsxAttributeName, + getTextOfNode, + getTextOfPropertyName, + getThisContainer, + getThisParameter, + getTrailingSemicolonDeferringWriter, + getTypeParameterFromJsDoc, + getUseDefineForClassFields, + hasAbstractModifier, + hasAccessorModifier, + hasAmbientModifier, + hasContextSensitiveParameters, + hasDecorators, + hasDynamicName, + hasEffectiveModifier, + hasEffectiveModifiers, + hasEffectiveReadonlyModifier, + hasJsonModuleEmitEnabled, + hasOverrideModifier, + hasPossibleExternalModuleReference, + hasQuestionToken, + hasStaticModifier, + hasSyntacticModifier, + hasSyntacticModifiers, + indexOfNode, + intrinsicTagNameToString, + introducesArgumentsExoticObject, + isAccessExpression, + isAliasableExpression, + isAmbientModule, + isAssignmentDeclaration, + isAssignmentExpression, + isAssignmentOperator, + isAssignmentTarget, + isAsyncFunction, + isBindableObjectDefinePropertyCall, + isBindableStaticElementAccessExpression, + isBindableStaticNameExpression, + isBindingElementOfBareOrAccessedRequire, + isBlockOrCatchScoped, + isBlockScopedContainerTopLevel, + isCatchClauseVariableDeclarationOrBindingElement, + isCheckJsEnabledForFile, + isCommonJsExportedExpression, + isCommonJsExportPropertyAssignment, + isComputedNonLiteralName, + isDeclarationName, + isDeclarationReadonly, + isDefaultedExpandoInitializer, + isDeleteTarget, + isDottedName, + isDynamicName, + isEffectiveExternalModule, + isEntityNameExpression, + isEnumConst, + isExportsIdentifier, + isExpressionNode, + isExpressionWithTypeArgumentsInClassExtendsClause, + isExternalModule, + isExternalModuleAugmentation, + isExternalModuleImportEqualsDeclaration, + isExternalOrCommonJsModule, + isFunctionExpressionOrArrowFunction, + isGlobalScopeAugmentation, + isIdentifierTypePredicate, + isIdentifierTypeReference, + isImportCall, + isInExpressionContext, + isInfinityOrNaNString, + isInJSDoc, + isInJSFile, + isInJsonFile, + isInternalModuleImportEqualsDeclaration, + isInTopLevelContext, + isIntrinsicJsxName, + isJSDocConstructSignature, + isJSDocIndexSignature, + isJSDocOptionalParameter, + isJSDocSatisfiesExpression, + isJSDocTypeAlias, + isJSDocTypeAssertion, + isJsonSourceFile, + isJSXTagName, + isKnownSymbol, + isLateVisibilityPaintedStatement, + isLet, + isLiteralComputedPropertyDeclarationName, + isLiteralImportTypeNode, + isLogicalOrCoalescingBinaryExpression, + isLogicalOrCoalescingBinaryOperator, + isModuleExportsAccessExpression, + isModuleIdentifier, + isModuleWithStringLiteralName, + isNamedEvaluationSource, + isNamespaceReexportDeclaration, + isNightly, + isNodeDescendantOf, + isNonNullAccess, + isNumericLiteralName, + isObjectLiteralMethod, + isObjectLiteralOrClassExpressionMethodOrAccessor, + isOptionalDeclaration, + isOptionalJSDocPropertyLikeTag, + isParameterDeclaration, + isParameterOrCatchClauseVariable, + isPartOfTypeNode, + isPartOfTypeQuery, + isPlainJsFile, + isPropertyAccessEntityNameExpression, + isPropertyNameLiteral, + isPrototypeAccess, + isPrototypePropertyAssignment, + isPushOrUnshiftIdentifier, + isRequireCall, + isRightSideOfQualifiedNameOrPropertyAccess, + isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName, + isSameEntityName, + isShorthandAmbientModuleSymbol, + isSingleOrDoubleQuote, + isSourceFileJS, + isStatementWithLocals, + isStatic, + isStringANonContextualKeyword, + isStringOrNumericLiteralLike, + isSuperCall, + isSuperProperty, + isThisContainerOrFunctionBlock, + isThisIdentifier, + isThisInitializedDeclaration, + isThisInitializedObjectBindingExpression, + isThisInTypeQuery, + isThisProperty, + isThisTypeParameter, + isTransientSymbol, + isTypeAlias, + isTypeDeclaration, + isTypeNodeKind, + isUMDExportSymbol, + isValidBigIntString, + isValidESSymbolDeclaration, + isValidTypeOnlyAliasUseSite, + isValueSignatureDeclaration, + isVarConst, + isVariableDeclarationInitializedToBareOrAccessedRequire, + isVariableDeclarationInVariableStatement, + isVariableLike, + isVariableLikeOrAccessor, + isWriteAccess, + isWriteOnlyAccess, + minAndMax, + modifiersToFlags, + modifierToFlag, + nodeCanBeDecorated, + nodeIsDecorated, + nodeIsMissing, + nodeIsPresent, + nodeIsSynthesized, + nodeStartsNewLexicalEnvironment, + noTruncationMaximumTruncationLength, + outFile, + parameterIsThisKeyword, + parseValidBigInt, + pseudoBigIntToString, + rangeOfNode, + rangeOfTypeParameters, + resolvingEmptyArray, + scanTokenAtPosition, + setNodeFlags, + setTextRangePosEnd, + setValueDeclaration, + shouldPreserveConstEnums, + shouldResolveJsRequire, + signatureHasLiteralTypes, + signatureHasRestParameter, + skipOuterExpressions, + skipParentheses, + skipTypeChecking, + stripQuotes, + tryGetClassImplementingOrExtendingExpressionWithTypeArguments, + tryGetJSDocSatisfiesTypeNode, + tryGetModuleSpecifierFromDeclaration, + tryGetPropertyAccessOrIdentifierToString, + usingSingleLineStringWriter, + walkUpOuterExpressions, + walkUpParenthesizedExpressions, + walkUpParenthesizedTypes, + walkUpParenthesizedTypesAndGetParentAndChild, +} from "./utilities"; +import { + canHaveLocals, + canHaveSymbol, + escapeLeadingUnderscores, + findAncestor, + getAllJSDocTags, + getCombinedModifierFlags, + getCombinedNodeFlags, + getDecorators, + getEffectiveConstraintOfTypeParameter, + getEffectiveTypeParameterDeclarations, + getJSDocClassTag, + getJSDocDeprecatedTag, + getJSDocEnumTag, + getJSDocParameterTags, + getJSDocTags, + getJSDocThisTag, + getJSDocType, + getJSDocTypeTag, + getModifiers, + getNameOfDeclaration, + getOriginalNode, + getParseTreeNode, + getTextOfJSDocComment, + hasInitializer, + hasJSDocNodes, + hasJSDocParameterTags, + hasOnlyExpressionInitializer, + hasRestParameter, + hasScopeMarker, + hasType, + identifierToKeywordKind, + idText, + isAccessor, + isAssertionExpression, + isAssignmentPattern, + isAutoAccessorPropertyDeclaration, + isBindingPattern, + isBooleanLiteral, + isCallChain, + isCallLikeExpression, + isCallOrNewExpression, + isClassElement, + isClassLike, + isConstTypeReference, + isDeclaration, + isEntityName, + isExpression, + isExpressionOfOptionalChainRoot, + isExternalModuleIndicator, + isExternalModuleNameRelative, + isForInOrOfStatement, + isFunctionLike, + isFunctionLikeDeclaration, + isFunctionLikeOrClassStaticBlockDeclaration, + isFunctionOrModuleBlock, + isGeneratedIdentifier, + isGetAccessor, + isGetOrSetAccessorDeclaration, + isImportOrExportSpecifier, + isIterationStatement, + isJSDocLinkLike, + isJSDocNode, + isJSDocPropertyLikeTag, + isJsxAttributeLike, + isJsxOpeningLikeElement, + isLeftHandSideExpression, + isLiteralExpression, + isLiteralExpressionOfObject, + isModifier, + isModuleOrEnumDeclaration, + isNamedDeclaration, + isNullishCoalesce, + isObjectLiteralElementLike, + isOptionalChain, + isOptionalChainRoot, + isOutermostOptionalChain, + isParameterPropertyDeclaration, + isPrivateIdentifierClassElementDeclaration, + isPrivateIdentifierPropertyAccessExpression, + isPropertyAccessOrQualifiedNameOrImportTypeNode, + isPropertyName, + isRestParameter, + isSetAccessor, + isStatement, + isStringLiteralLike, + isTypeNode, + isTypeOnlyImportOrExportDeclaration, + isTypeReferenceType, + needsScopeMarker, + nodeHasName, + ParameterPropertyDeclaration, + symbolName, + textRangeContainsPositionInclusive, + textSpanContainsPosition, + textSpanEnd, + unescapeLeadingUnderscores, + walkUpBindingElementsAndPatterns, +} from "./utilitiesPublic"; +import { + visitEachChild, + visitNode, + visitNodes, +} from "./visitorPublic"; const ambientModuleSymbolRegex = /^".+"$/; const anon = "(anonymous)" as __String & string; -let nextSymbolId = 1; -let nextNodeId = 1; let nextMergeId = 1; let nextFlowId = 1; @@ -1138,90 +1178,6 @@ const enum WideningKind { GeneratorYield, } -/** @internal */ -export const enum TypeFacts { - None = 0, - TypeofEQString = 1 << 0, // typeof x === "string" - TypeofEQNumber = 1 << 1, // typeof x === "number" - TypeofEQBigInt = 1 << 2, // typeof x === "bigint" - TypeofEQBoolean = 1 << 3, // typeof x === "boolean" - TypeofEQSymbol = 1 << 4, // typeof x === "symbol" - TypeofEQObject = 1 << 5, // typeof x === "object" - TypeofEQFunction = 1 << 6, // typeof x === "function" - TypeofEQHostObject = 1 << 7, // typeof x === "xxx" - TypeofNEString = 1 << 8, // typeof x !== "string" - TypeofNENumber = 1 << 9, // typeof x !== "number" - TypeofNEBigInt = 1 << 10, // typeof x !== "bigint" - TypeofNEBoolean = 1 << 11, // typeof x !== "boolean" - TypeofNESymbol = 1 << 12, // typeof x !== "symbol" - TypeofNEObject = 1 << 13, // typeof x !== "object" - TypeofNEFunction = 1 << 14, // typeof x !== "function" - TypeofNEHostObject = 1 << 15, // typeof x !== "xxx" - EQUndefined = 1 << 16, // x === undefined - EQNull = 1 << 17, // x === null - EQUndefinedOrNull = 1 << 18, // x === undefined / x === null - NEUndefined = 1 << 19, // x !== undefined - NENull = 1 << 20, // x !== null - NEUndefinedOrNull = 1 << 21, // x != undefined / x != null - Truthy = 1 << 22, // x - Falsy = 1 << 23, // !x - IsUndefined = 1 << 24, // Contains undefined or intersection with undefined - IsNull = 1 << 25, // Contains null or intersection with null - IsUndefinedOrNull = IsUndefined | IsNull, - All = (1 << 27) - 1, - // The following members encode facts about particular kinds of types for use in the getTypeFacts function. - // The presence of a particular fact means that the given test is true for some (and possibly all) values - // of that kind of type. - BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, - BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy, - StringFacts = BaseStringFacts | Truthy, - EmptyStringStrictFacts = BaseStringStrictFacts | Falsy, - EmptyStringFacts = BaseStringFacts, - NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy, - NonEmptyStringFacts = BaseStringFacts | Truthy, - BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, - BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy, - NumberFacts = BaseNumberFacts | Truthy, - ZeroNumberStrictFacts = BaseNumberStrictFacts | Falsy, - ZeroNumberFacts = BaseNumberFacts, - NonZeroNumberStrictFacts = BaseNumberStrictFacts | Truthy, - NonZeroNumberFacts = BaseNumberFacts | Truthy, - BaseBigIntStrictFacts = TypeofEQBigInt | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, - BaseBigIntFacts = BaseBigIntStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - BigIntStrictFacts = BaseBigIntStrictFacts | Truthy | Falsy, - BigIntFacts = BaseBigIntFacts | Truthy, - ZeroBigIntStrictFacts = BaseBigIntStrictFacts | Falsy, - ZeroBigIntFacts = BaseBigIntFacts, - NonZeroBigIntStrictFacts = BaseBigIntStrictFacts | Truthy, - NonZeroBigIntFacts = BaseBigIntFacts | Truthy, - BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, - BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy, - BooleanFacts = BaseBooleanFacts | Truthy, - FalseStrictFacts = BaseBooleanStrictFacts | Falsy, - FalseFacts = BaseBooleanFacts, - TrueStrictFacts = BaseBooleanStrictFacts | Truthy, - TrueFacts = BaseBooleanFacts | Truthy, - SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, - SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, - ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, - FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - VoidFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, - UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy | IsUndefined, - NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy | IsNull, - EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull | IsUndefinedOrNull), - EmptyObjectFacts = All & ~IsUndefinedOrNull, - UnknownFacts = All & ~IsUndefinedOrNull, - AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined, - // Masks - OrFactsMask = TypeofEQFunction | TypeofNEObject, - AndFactsMask = All & ~OrFactsMask, -} - const typeofNEFacts: ReadonlyMap = new Map(Object.entries({ string: TypeFacts.TypeofNEString, number: TypeFacts.TypeofNENumber, @@ -1248,31 +1204,6 @@ const enum TypeSystemPropertyName { ParameterInitializerContainsUndefined, } -/** @internal */ -export const enum CheckMode { - Normal = 0, // Normal type checking - Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable - Inferential = 1 << 1, // Inferential typing - SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions - SkipGenericFunctions = 1 << 3, // Skip single signature generic functions - IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help - IsForStringLiteralArgumentCompletions = 1 << 5, // Do not infer from the argument currently being typed - RestBindingElement = 1 << 6, // Checking a type that is going to be used to determine the type of a rest binding element - // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`, - // we need to preserve generic types instead of substituting them for constraints -} - -/** @internal */ -export const enum SignatureCheckMode { - None = 0, - BivariantCallback = 1 << 0, - StrictCallback = 1 << 1, - IgnoreReturnTypes = 1 << 2, - StrictArity = 1 << 3, - StrictTopSignature = 1 << 4, - Callback = BivariantCallback | StrictCallback, -} - const enum IntersectionState { None = 0, Source = 1 << 0, // Source type is a constituent of an outer intersection @@ -1360,25 +1291,6 @@ function NodeLinks(this: NodeLinks) { this.flags = NodeCheckFlags.None; } -/** @internal */ -export function getNodeId(node: Node): number { - if (!node.id) { - node.id = nextNodeId; - nextNodeId++; - } - return node.id; -} - -/** @internal */ -export function getSymbolId(symbol: Symbol): SymbolId { - if (!symbol.id) { - symbol.id = nextSymbolId; - nextSymbolId++; - } - - return symbol.id; -} - /** @internal */ export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) { const moduleState = getModuleInstanceState(node); @@ -4892,7 +4804,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (resolutionDiagnostic) { error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.resolvedFileName); } - if (resolvedModule.resolvedUsingTsExtension && isDeclarationFileName(moduleReference)) { const importOrExport = findAncestor(location, isImportDeclaration)?.importClause || @@ -4908,7 +4819,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const tsExtension = Debug.checkDefined(tryExtractTSExtension(moduleReference)); error(errorNode, Diagnostics.An_import_path_can_only_end_with_a_0_extension_when_allowImportingTsExtensions_is_enabled, tsExtension); } - if (sourceFile.symbol) { if (resolvedModule.isExternalLibraryImport && !resolutionExtensionIsTSOrJson(resolvedModule.extension)) { errorOnImplicitAnyModule(/*isError*/ false, errorNode, currentSourceFile, mode, resolvedModule, moduleReference); @@ -7645,7 +7555,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const isBRelative = pathIsRelative(specifierB); if (pathIsRelative(specifierA) === isBRelative) { // Both relative or both non-relative, sort by number of parts - return moduleSpecifiers.countPathComponents(specifierA) - moduleSpecifiers.countPathComponents(specifierB); + return countPathComponents(specifierA) - countPathComponents(specifierB); } if (isBRelative) { // A is non-relative, B is relative: prefer A @@ -7749,7 +7659,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // specifier preference const { moduleResolverHost } = context.tracker; const specifierCompilerOptions = isBundle ? { ...compilerOptions, baseUrl: moduleResolverHost.getCommonSourceDirectory() } : compilerOptions; - specifier = first(moduleSpecifiers.getModuleSpecifiers( + specifier = first(getModuleSpecifiers( symbol, checker, specifierCompilerOptions, @@ -8074,7 +7984,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return fromNameType; } const rawName = unescapeLeadingUnderscores(symbol.escapedName); - return createPropertyNameNodeForIdentifierOrLiteral(rawName, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed); + return createPropertyNameNodeForIdentifierOrLiteral(factory, rawName, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed); } // See getNameForSymbolFromNameType for a stringy equivalent @@ -8089,7 +7999,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isNumericLiteralName(name) && startsWith(name, "-")) { return factory.createComputedPropertyName(factory.createNumericLiteral(+name)); } - return createPropertyNameNodeForIdentifierOrLiteral(name, getEmitScriptTarget(compilerOptions)); + return createPropertyNameNodeForIdentifierOrLiteral(factory, name, getEmitScriptTarget(compilerOptions)); } if (nameType.flags & TypeFlags.UniqueESSymbol) { return factory.createComputedPropertyName(symbolToExpression((nameType as UniqueESSymbolType).symbol, context, SymbolFlags.Value)); @@ -48942,16 +48852,6 @@ function getIterationTypesKeyFromIterationTypeKind(typeKind: IterationTypeKind) } } -/** @internal */ -export function signatureHasRestParameter(s: Signature) { - return !!(s.flags & SignatureFlags.HasRestParameter); -} - -/** @internal */ -export function signatureHasLiteralTypes(s: Signature) { - return !!(s.flags & SignatureFlags.HasLiteralTypes); -} - function createBasicNodeBuilderModuleSpecifierResolutionHost(host: TypeCheckerHost): ModuleSpecifierResolutionHost & { getCommonSourceDirectory(): string } { return { getCommonSourceDirectory: !!(host as Program).getCommonSourceDirectory ? () => (host as Program).getCommonSourceDirectory() : () => "", diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 3b2120d258c8c..f28122c610cca 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1,42 +1,13 @@ import { - AlternateModeDiagnostics, append, arrayFrom, - ArrayLiteralExpression, arrayToMap, assign, - BuildOptions, - changeExtension, - CharacterCodes, - combinePaths, - CommandLineOption, - CommandLineOptionOfCustomType, - CommandLineOptionOfListType, - CompilerOptions, - CompilerOptionsValue, - ConfigFileSpecs, - containsPath, - convertToRelativePath, - createCompilerDiagnostic, - createDiagnosticForNodeInSourceFile, createGetCanonicalFileName, - Debug, - Diagnostic, - DiagnosticArguments, - DiagnosticMessage, - Diagnostics, - DidYouMeanOptionsDiagnostics, - directorySeparator, emptyArray, endsWith, - ensureTrailingDirectorySeparator, every, - Expression, extend, - Extension, - FileExtensionInfo, - fileExtensionIs, - fileExtensionIsOneOf, filter, filterMutate, find, @@ -44,82 +15,123 @@ import { firstOrUndefinedIterator, flatten, forEach, - forEachEntry, - forEachTsConfigPropArray, - getBaseFileName, - getDirectoryPath, + getSpellingSuggestion, + hasProperty, + isArray, + isString, + length, + map, + mapDefined, + mapIterator, + returnTrue, + startsWith, + stringContains, + toFileNameLowerCase, + trimString, +} from "./core"; +import { MapLike } from "./corePublic"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { + changeExtension, + getSupportedExtensions, + getSupportedExtensionsWithJsonIfResolveJsonModule, +} from "./extension"; +import { + isArrayLiteralExpression, + isObjectLiteralExpression, + isStringLiteral, +} from "./factory/nodeTests"; +import { getFileMatcherPatterns, - getLocaleSpecificMessage, - getNormalizedAbsolutePath, getRegexFromPattern, getRegularExpressionForWildcard, getRegularExpressionsForWildcards, + isImplicitGlob, +} from "./fileMatcher"; +import { nodeNextJsonConfigResolver } from "./moduleNameResolver"; +import { parseJsonText } from "./parser"; +import { + combinePaths, + containsPath, + convertToRelativePath, + directorySeparator, + ensureTrailingDirectorySeparator, + fileExtensionIs, + fileExtensionIsOneOf, + getBaseFileName, + getDirectoryPath, + getNormalizedAbsolutePath, getRelativePathFromFile, - getSpellingSuggestion, - getSupportedExtensions, - getSupportedExtensionsWithJsonIfResolveJsonModule, - getTextOfPropertyName, - getTsConfigPropArrayElementValue, hasExtension, - hasProperty, - ImportsNotUsedAsValues, - isArray, - isArrayLiteralExpression, - isComputedNonLiteralName, - isImplicitGlob, - isObjectLiteralExpression, isRootedDiskPath, - isString, - isStringDoubleQuoted, - isStringLiteral, + normalizePath, + normalizeSlashes, + removeTrailingDirectorySeparator, + toPath, +} from "./path"; +import { sys } from "./sys"; +import { tracing } from "./tracing"; +import { BuildOptions } from "./tsbuildPublic"; +import { + AlternateModeDiagnostics, + ArrayLiteralExpression, + CharacterCodes, + CommandLineOption, + CommandLineOptionOfCustomType, + CommandLineOptionOfListType, + CompilerOptions, + CompilerOptionsValue, + ConfigFileSpecs, + Diagnostic, + DiagnosticArguments, + DiagnosticMessage, + DidYouMeanOptionsDiagnostics, + Expression, + Extension, + FileExtensionInfo, + ImportsNotUsedAsValues, JsonSourceFile, JsxEmit, - length, - map, - mapDefined, - mapIterator, - MapLike, ModuleDetectionKind, ModuleKind, ModuleResolutionKind, NewLineKind, Node, NodeArray, - nodeNextJsonConfigResolver, - normalizePath, - normalizeSlashes, NumericLiteral, ObjectLiteralExpression, ParseConfigHost, ParsedCommandLine, - parseJsonText, Path, PollingWatchKind, PrefixUnaryExpression, ProjectReference, PropertyAssignment, PropertyName, - removeTrailingDirectorySeparator, - returnTrue, ScriptTarget, - startsWith, - stringContains, StringLiteral, SyntaxKind, - sys, - toFileNameLowerCase, - toPath, - tracing, - trimString, TsConfigOnlyOption, TsConfigSourceFile, TypeAcquisition, - unescapeLeadingUnderscores, WatchDirectoryFlags, WatchDirectoryKind, WatchFileKind, WatchOptions, -} from "./_namespaces/ts"; +} from "./types"; +import { + createCompilerDiagnostic, + createDiagnosticForNodeInSourceFile, + forEachEntry, + forEachTsConfigPropArray, + getLocaleSpecificMessage, + getTextOfPropertyName, + getTsConfigPropArrayElementValue, + isComputedNonLiteralName, + isStringDoubleQuoted, +} from "./utilities"; +import { unescapeLeadingUnderscores } from "./utilitiesPublic"; /** @internal */ export const compileOnSaveCommandLineOption: CommandLineOption = { diff --git a/src/compiler/commandLineParserUtilities.ts b/src/compiler/commandLineParserUtilities.ts new file mode 100644 index 0000000000000..cc7181c9c97df --- /dev/null +++ b/src/compiler/commandLineParserUtilities.ts @@ -0,0 +1,16 @@ +import { parseConfigFileTextToJson } from "./commandLineParser"; +import { isString } from "./core"; + +/** @internal */ +export function readJsonOrUndefined(path: string, hostOrText: { readFile(fileName: string): string | undefined } | string): object | undefined { + const jsonText = isString(hostOrText) ? hostOrText : hostOrText.readFile(path); + if (!jsonText) return undefined; + // gracefully handle if readFile fails or returns not JSON + const result = parseConfigFileTextToJson(path, jsonText); + return !result.error ? result.config : undefined; +} + +/** @internal */ +export function readJson(path: string, host: { readFile(fileName: string): string | undefined }): object { + return readJsonOrUndefined(path, host) || {}; +} diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 1acb342daa494..c0d1f7557c561 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1,18 +1,18 @@ import { - __String, - CharacterCodes, Comparer, Comparison, - Debug, EqualityComparer, - isWhiteSpaceLike, MapLike, - Queue, SortedArray, SortedReadonlyArray, +} from "./corePublic"; +import * as Debug from "./debug"; +import { + __String, + CharacterCodes, + Queue, TextSpan, -} from "./_namespaces/ts"; - +} from "./types"; /** @internal */ export const emptyArray: never[] = [] as never[]; @@ -1870,7 +1870,6 @@ export function tryCast(value: TIn | undefined, tes /** @internal */ export function cast(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut { if (value !== undefined && test(value)) return value; - return Debug.fail(`Invalid cast. The supplied value ${value} did not pass the test '${Debug.getFunctionName(test)}'.`); } @@ -2082,23 +2081,6 @@ export function compose(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t return t => t; } } -/** @internal */ -export const enum AssertionLevel { - None = 0, - Normal = 1, - Aggressive = 2, - VeryAggressive = 3, -} - -/** - * Safer version of `Function` which should not be called. - * Every function should be assignable to this, but this should not be assignable to every function. - * - * @internal - */ -export type AnyFunction = (...args: never[]) => void; -/** @internal */ -export type AnyConstructor = new (...args: unknown[]) => unknown; /** @internal */ export function equateValues(a: T, b: T) { @@ -2869,16 +2851,42 @@ function trimEndImpl(s: string) { return s.slice(0, end + 1); } -/** @internal */ -export function isNodeLikeSystem(): boolean { - // This is defined here rather than in sys.ts to prevent a cycle from its - // use in performanceCore.ts. - // - // We don't use the presence of `require` to check if we are in Node; - // when bundled using esbuild, this function will be rewritten to `__require` - // and definitely exist. - return typeof process !== "undefined" - && !!process.nextTick - && !(process as any).browser - && typeof module === "object"; +export function isWhiteSpaceLike(ch: number): boolean { + return isWhiteSpaceSingleLine(ch) || isLineBreak(ch); +} + +/** Does not include line breaks. For that, see isWhiteSpaceLike. */ +export function isWhiteSpaceSingleLine(ch: number): boolean { + // Note: nextLine is in the Zs space, and should be considered to be a whitespace. + // It is explicitly not a line-break as it isn't in the exact set specified by EcmaScript. + return ch === CharacterCodes.space || + ch === CharacterCodes.tab || + ch === CharacterCodes.verticalTab || + ch === CharacterCodes.formFeed || + ch === CharacterCodes.nonBreakingSpace || + ch === CharacterCodes.nextLine || + ch === CharacterCodes.ogham || + ch >= CharacterCodes.enQuad && ch <= CharacterCodes.zeroWidthSpace || + ch === CharacterCodes.narrowNoBreakSpace || + ch === CharacterCodes.mathematicalSpace || + ch === CharacterCodes.ideographicSpace || + ch === CharacterCodes.byteOrderMark; +} + +export function isLineBreak(ch: number): boolean { + // ES5 7.3: + // The ECMAScript line terminator characters are listed in Table 3. + // Table 3: Line Terminator Characters + // Code Unit Value Name Formal Name + // \u000A Line Feed + // \u000D Carriage Return + // \u2028 Line separator + // \u2029 Paragraph separator + // Only the characters in Table 3 are treated as line terminators. Other new line or line + // breaking characters are treated as white space but not as line terminators. + + return ch === CharacterCodes.lineFeed || + ch === CharacterCodes.carriageReturn || + ch === CharacterCodes.lineSeparator || + ch === CharacterCodes.paragraphSeparator; } diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index 94838e3f8634a..e942248316e23 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -1,26 +1,19 @@ -import * as ts from "./_namespaces/ts"; +/* eslint-disable */ +import * as types from "./types"; +/* eslint-enable */ + import { - AnyFunction, - AssertionLevel, - BigIntLiteralType, - CheckMode, compareValues, - EmitFlags, every, - FlowFlags, - FlowLabel, - FlowNode, - FlowNodeBase, - FlowSwitchClause, - getEffectiveModifierFlagsNoCache, - getEmitFlags, getOwnKeys, - getParseTreeNode, - getSourceFileOfNode, - getSourceTextOfNodeFromSourceFile, hasProperty, - idText, - IntrinsicType, + map, + noop, + stableSort, + zipWith, +} from "./core"; +import { SortedReadonlyArray } from "./corePublic"; +import { isArrayTypeNode, isBigIntLiteral, isCallSignatureDeclaration, @@ -30,7 +23,6 @@ import { isConstructSignatureDeclaration, isDefaultClause, isFunctionTypeNode, - isGeneratedIdentifier, isGetAccessorDeclaration, isIdentifier, isImportTypeNode, @@ -45,7 +37,6 @@ import { isOptionalTypeNode, isParameter, isParenthesizedTypeNode, - isParseTreeNode, isPrivateIdentifier, isRestTypeNode, isSetAccessorDeclaration, @@ -59,39 +50,57 @@ import { isTypeQueryNode, isTypeReferenceNode, isUnionTypeNode, +} from "./factory/nodeTests"; +import { objectAllocator } from "./objectAllocator"; +import { + AnyFunction, + BigIntLiteralType, + EmitFlags, + FlowFlags, + FlowLabel, + FlowNode, + FlowNodeBase, + FlowSwitchClause, + IntrinsicType, LiteralType, - map, - MatchingKeys, ModifierFlags, Node, NodeArray, NodeFlags, - nodeIsSynthesized, - noop, - objectAllocator, ObjectFlags, ObjectType, RelationComparisonResult, Signature, - SignatureCheckMode, SignatureFlags, SnippetKind, - SortedReadonlyArray, - stableSort, Symbol, SymbolFlags, - symbolName, SyntaxKind, TransformFlags, Type, - TypeFacts, TypeFlags, TypeMapKind, TypeMapper, - unescapeLeadingUnderscores, VarianceFlags, - zipWith, -} from "./_namespaces/ts"; + CheckMode, + SignatureCheckMode, + TypeFacts, +} from "./types"; +import { + getEffectiveModifierFlagsNoCache, + getEmitFlags, + getSourceFileOfNode, + getSourceTextOfNodeFromSourceFile, + nodeIsSynthesized, +} from "./utilities"; +import { + getParseTreeNode, + idText, + isGeneratedIdentifier, + isParseTreeNode, + symbolName, + unescapeLeadingUnderscores, +} from "./utilitiesPublic"; /** @internal */ export enum LogLevel { @@ -108,1063 +117,1168 @@ export interface LoggingHost { } /** @internal */ -export namespace Debug { - /* eslint-disable prefer-const */ - let currentAssertionLevel = AssertionLevel.None; - export let currentLogLevel = LogLevel.Warning; - export let isDebugging = false; - export let loggingHost: LoggingHost | undefined; - /* eslint-enable prefer-const */ - - type AssertionKeys = MatchingKeys; - - export function shouldLog(level: LogLevel): boolean { - return currentLogLevel <= level; - } +export const enum AssertionLevel { + None = 0, + Normal = 1, + Aggressive = 2, + VeryAggressive = 3, +} - function logMessage(level: LogLevel, s: string): void { - if (loggingHost && shouldLog(level)) { - loggingHost.log(level, s); - } - } +let currentAssertionLevel = AssertionLevel.None; - export function log(s: string): void { - logMessage(LogLevel.Info, s); - } +/** @internal */ +export const currentLogLevel = LogLevel.Warning; - export namespace log { - export function error(s: string): void { - logMessage(LogLevel.Error, s); - } +/** @internal */ +export let isDebugging = false; - export function warn(s: string): void { - logMessage(LogLevel.Warning, s); - } +/** @internal */ +export function setIsDebugging(newIsDebugging: boolean) { + isDebugging = newIsDebugging; +} - export function log(s: string): void { - logMessage(LogLevel.Info, s); - } +/** @internal */ +export let loggingHost: LoggingHost | undefined; - export function trace(s: string): void { - logMessage(LogLevel.Verbose, s); - } - } +/** @internal */ +export function setLoggingHost(newLoggingHost: LoggingHost | undefined) { + loggingHost = newLoggingHost; +} - const assertionCache: Partial> = {}; +/** @internal */ +export function shouldLog(level: LogLevel): boolean { + return currentLogLevel <= level; +} - export function getAssertionLevel() { - return currentAssertionLevel; +function logMessage(level: LogLevel, s: string): void { + if (loggingHost && shouldLog(level)) { + loggingHost.log(level, s); } +} - export function setAssertionLevel(level: AssertionLevel) { - const prevAssertionLevel = currentAssertionLevel; - currentAssertionLevel = level; - - if (level > prevAssertionLevel) { - // restore assertion functions for the current assertion level (see `shouldAssertFunction`). - for (const key of getOwnKeys(assertionCache) as AssertionKeys[]) { - const cachedFunc = assertionCache[key]; - if (cachedFunc !== undefined && Debug[key] !== cachedFunc.assertion && level >= cachedFunc.level) { - (Debug as any)[key] = cachedFunc; - assertionCache[key] = undefined; - } - } - } - } +/** @internal */ +export function log(s: string): void { + logMessage(LogLevel.Info, s); +} - export function shouldAssert(level: AssertionLevel): boolean { - return currentAssertionLevel >= level; +/** @internal */ +export namespace log { + export function error(s: string): void { + logMessage(LogLevel.Error, s); } - /** - * Tests whether an assertion function should be executed. If it shouldn't, it is cached and replaced with `ts.noop`. - * Replaced assertion functions are restored when `Debug.setAssertionLevel` is set to a high enough level. - * @param level The minimum assertion level required. - * @param name The name of the current assertion function. - */ - function shouldAssertFunction(level: AssertionLevel, name: K): boolean { - if (!shouldAssert(level)) { - assertionCache[name] = { level, assertion: Debug[name] }; - (Debug as any)[name] = noop; - return false; - } - return true; + export function warn(s: string): void { + logMessage(LogLevel.Warning, s); } - export function fail(message?: string, stackCrawlMark?: AnyFunction): never { - debugger; - const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure."); - if ((Error as any).captureStackTrace) { - (Error as any).captureStackTrace(e, stackCrawlMark || fail); - } - throw e; + export function log(s: string): void { + logMessage(LogLevel.Info, s); } - export function failBadSyntaxKind(node: Node, message?: string, stackCrawlMark?: AnyFunction): never { - return fail( - `${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`, - stackCrawlMark || failBadSyntaxKind); + export function trace(s: string): void { + logMessage(LogLevel.Verbose, s); } +} - export function assert(expression: unknown, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): asserts expression { - if (!expression) { - message = message ? `False expression: ${message}` : "False expression."; - if (verboseDebugInfo) { - message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo()); - } - fail(message, stackCrawlMark || assert); - } - } +/** @internal */ +export function getAssertionLevel() { + return currentAssertionLevel; +} - export function assertEqual(a: T, b: T, msg?: string, msg2?: string, stackCrawlMark?: AnyFunction): void { - if (a !== b) { - const message = msg ? msg2 ? `${msg} ${msg2}` : msg : ""; - fail(`Expected ${a} === ${b}. ${message}`, stackCrawlMark || assertEqual); +/** @internal */ +export function setAssertionLevel(level: AssertionLevel) { + const prevAssertionLevel = currentAssertionLevel; + currentAssertionLevel = level; + + if (level > prevAssertionLevel) { + // restore assertion functions for the current assertion level (see `shouldAssertFunction`). + for (const name of getOwnKeys(assertionCache) as AssertionKeys[]) { + assertionCache[name].enable(level); } } +} - export function assertLessThan(a: number, b: number, msg?: string, stackCrawlMark?: AnyFunction): void { - if (a >= b) { - fail(`Expected ${a} < ${b}. ${msg || ""}`, stackCrawlMark || assertLessThan); - } +/** @internal */ +export function shouldAssert(level: AssertionLevel): boolean { + return currentAssertionLevel >= level; +} + +/** + * Tests whether an assertion function should be executed. If it shouldn't, it is cached and replaced with `ts.noop`. + * Replaced assertion functions are restored when `Debug.setAssertionLevel` is set to a high enough level. + * @param level The minimum assertion level required. + * @param name The name of the current assertion function. + */ +function shouldAssertFunction(level: AssertionLevel, name: K): boolean { + if (!shouldAssert(level)) { + assertionCache[name].disable(level); + return false; } + return true; +} - export function assertLessThanOrEqual(a: number, b: number, stackCrawlMark?: AnyFunction): void { - if (a > b) { - fail(`Expected ${a} <= ${b}`, stackCrawlMark || assertLessThanOrEqual); - } +/** @internal */ +export function fail(message?: string, stackCrawlMark?: AnyFunction): never { + debugger; + const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure."); + if ((Error as any).captureStackTrace) { + (Error as any).captureStackTrace(e, stackCrawlMark || fail); } + throw e; +} + +/** @internal */ +export function failBadSyntaxKind(node: Node, message?: string, stackCrawlMark?: AnyFunction): never { + return fail( + `${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`, + stackCrawlMark || failBadSyntaxKind); +} - export function assertGreaterThanOrEqual(a: number, b: number, stackCrawlMark?: AnyFunction): void { - if (a < b) { - fail(`Expected ${a} >= ${b}`, stackCrawlMark || assertGreaterThanOrEqual); +/** @internal */ +export function assert(expression: unknown, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): asserts expression { + if (!expression) { + message = message ? `False expression: ${message}` : "False expression."; + if (verboseDebugInfo) { + message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo()); } + fail(message, stackCrawlMark || assert); } +} - export function assertIsDefined(value: T, message?: string, stackCrawlMark?: AnyFunction): asserts value is NonNullable { - // eslint-disable-next-line no-null/no-null - if (value === undefined || value === null) { - fail(message, stackCrawlMark || assertIsDefined); - } +/** @internal */ +export function assertEqual(a: T, b: T, msg?: string, msg2?: string, stackCrawlMark?: AnyFunction): void { + if (a !== b) { + const message = msg ? msg2 ? `${msg} ${msg2}` : msg : ""; + fail(`Expected ${a} === ${b}. ${message}`, stackCrawlMark || assertEqual); } +} - export function checkDefined(value: T | null | undefined, message?: string, stackCrawlMark?: AnyFunction): T { - assertIsDefined(value, message, stackCrawlMark || checkDefined); - return value; +/** @internal */ +export function assertLessThan(a: number, b: number, msg?: string, stackCrawlMark?: AnyFunction): void { + if (a >= b) { + fail(`Expected ${a} < ${b}. ${msg || ""}`, stackCrawlMark || assertLessThan); } +} - export function assertEachIsDefined(value: NodeArray, message?: string, stackCrawlMark?: AnyFunction): asserts value is NodeArray; - export function assertEachIsDefined(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction): asserts value is readonly NonNullable[]; - export function assertEachIsDefined(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction) { - for (const v of value) { - assertIsDefined(v, message, stackCrawlMark || assertEachIsDefined); - } +/** @internal */ +export function assertLessThanOrEqual(a: number, b: number, stackCrawlMark?: AnyFunction): void { + if (a > b) { + fail(`Expected ${a} <= ${b}`, stackCrawlMark || assertLessThanOrEqual); } +} - export function checkEachDefined(value: A, message?: string, stackCrawlMark?: AnyFunction): A { - assertEachIsDefined(value, message, stackCrawlMark || checkEachDefined); - return value; +/** @internal */ +export function assertGreaterThanOrEqual(a: number, b: number, stackCrawlMark?: AnyFunction): void { + if (a < b) { + fail(`Expected ${a} >= ${b}`, stackCrawlMark || assertGreaterThanOrEqual); } +} - export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never { - const detail = typeof member === "object" && hasProperty(member, "kind") && hasProperty(member, "pos") ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member); - return fail(`${message} ${detail}`, stackCrawlMark || assertNever); +/** @internal */ +export function assertIsDefined(value: T, message?: string, stackCrawlMark?: AnyFunction): asserts value is NonNullable { + // eslint-disable-next-line no-null/no-null + if (value === undefined || value === null) { + fail(message, stackCrawlMark || assertIsDefined); } +} - export function assertEachNode(nodes: NodeArray, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is NodeArray; - export function assertEachNode(nodes: readonly T[], test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[]; - export function assertEachNode(nodes: NodeArray | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is NodeArray | undefined; - export function assertEachNode(nodes: readonly T[] | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[] | undefined; - export function assertEachNode(nodes: readonly Node[], test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; - export function assertEachNode(nodes: readonly Node[] | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertEachNode")) { - assert( - test === undefined || every(nodes, test), - message || "Unexpected node.", - () => `Node array did not pass test '${getFunctionName(test!)}'.`, - stackCrawlMark || assertEachNode); - } +/** @internal */ +export function checkDefined(value: T | null | undefined, message?: string, stackCrawlMark?: AnyFunction): T { + assertIsDefined(value, message, stackCrawlMark || checkDefined); + return value; +} + +/** @internal */ +export function assertEachIsDefined(value: NodeArray, message?: string, stackCrawlMark?: AnyFunction): asserts value is NodeArray; +/** @internal */ +export function assertEachIsDefined(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction): asserts value is readonly NonNullable[]; +export function assertEachIsDefined(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction) { + for (const v of value) { + assertIsDefined(v, message, stackCrawlMark || assertEachIsDefined); } +} - export function assertNode(node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U; - export function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; - export function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertNode")) { - assert( - node !== undefined && (test === undefined || test(node)), - message || "Unexpected node.", - () => `Node ${formatSyntaxKind(node?.kind)} did not pass test '${getFunctionName(test!)}'.`, - stackCrawlMark || assertNode); - } +/** @internal */ +export function checkEachDefined(value: A, message?: string, stackCrawlMark?: AnyFunction): A { + assertEachIsDefined(value, message, stackCrawlMark || checkEachDefined); + return value; +} + +/** @internal */ +export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never { + const detail = typeof member === "object" && hasProperty(member, "kind") && hasProperty(member, "pos") ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member); + return fail(`${message} ${detail}`, stackCrawlMark || assertNever); +} + +/** @internal */ +export let assertEachNode: { + (nodes: NodeArray, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is NodeArray; + (nodes: readonly T[], test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[]; + (nodes: NodeArray | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is NodeArray | undefined; + (nodes: readonly T[] | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[] | undefined; + (nodes: readonly Node[], test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; +} = (nodes: readonly Node[] | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) => { + if (shouldAssertFunction(AssertionLevel.Normal, "assertEachNode")) { + assert( + test === undefined || every(nodes, test), + message || "Unexpected node.", + () => `Node array did not pass test '${getFunctionName(test!)}'.`, + stackCrawlMark || assertEachNode); } +}; - export function assertNotNode(node: T | undefined, test: (node: Node) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is Exclude; - export function assertNotNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; - export function assertNotNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertNotNode")) { - assert( - node === undefined || test === undefined || !test(node), - message || "Unexpected node.", - () => `Node ${formatSyntaxKind(node!.kind)} should not have passed test '${getFunctionName(test!)}'.`, - stackCrawlMark || assertNotNode); - } +/** @internal */ +export let assertNode: { + (node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U; + (node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; +} = (node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) => { + if (shouldAssertFunction(AssertionLevel.Normal, "assertNode")) { + assert( + node !== undefined && (test === undefined || test(node)), + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node?.kind)} did not pass test '${getFunctionName(test!)}'.`, + stackCrawlMark || assertNode); } +}; - export function assertOptionalNode(node: T, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U; - export function assertOptionalNode(node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U | undefined; - export function assertOptionalNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; - export function assertOptionalNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalNode")) { - assert( - test === undefined || node === undefined || test(node), - message || "Unexpected node.", - () => `Node ${formatSyntaxKind(node?.kind)} did not pass test '${getFunctionName(test!)}'.`, - stackCrawlMark || assertOptionalNode); - } +/** @internal */ +export let assertNotNode: { + (node: T | undefined, test: (node: Node) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is Exclude; + (node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; +} = (node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) => { + if (shouldAssertFunction(AssertionLevel.Normal, "assertNotNode")) { + assert( + node === undefined || test === undefined || !test(node), + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node!.kind)} should not have passed test '${getFunctionName(test!)}'.`, + stackCrawlMark || assertNotNode); } +}; - export function assertOptionalToken(node: T, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract; - export function assertOptionalToken(node: T | undefined, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract | undefined; - export function assertOptionalToken(node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction): void; - export function assertOptionalToken(node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalToken")) { - assert( - kind === undefined || node === undefined || node.kind === kind, - message || "Unexpected node.", - () => `Node ${formatSyntaxKind(node?.kind)} was not a '${formatSyntaxKind(kind)}' token.`, - stackCrawlMark || assertOptionalToken); - } +/** @internal */ +export let assertOptionalNode: { + (node: T, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U; + (node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U | undefined; + (node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; +} = (node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) => { + if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalNode")) { + assert( + test === undefined || node === undefined || test(node), + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node?.kind)} did not pass test '${getFunctionName(test!)}'.`, + stackCrawlMark || assertOptionalNode); } +}; - export function assertMissingNode(node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction): asserts node is undefined; - export function assertMissingNode(node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction) { - if (shouldAssertFunction(AssertionLevel.Normal, "assertMissingNode")) { - assert( - node === undefined, - message || "Unexpected node.", - () => `Node ${formatSyntaxKind(node!.kind)} was unexpected'.`, - stackCrawlMark || assertMissingNode); - } +/** @internal */ +export let assertOptionalToken: { + (node: T, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract; + (node: T | undefined, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract | undefined; + (node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction): void; +} = (node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction) => { + if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalToken")) { + assert( + kind === undefined || node === undefined || node.kind === kind, + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node?.kind)} was not a '${formatSyntaxKind(kind)}' token.`, + stackCrawlMark || assertOptionalToken); } +}; - /** - * Asserts a value has the specified type in typespace only (does not perform a runtime assertion). - * This is useful in cases where we switch on `node.kind` and can be reasonably sure the type is accurate, and - * as a result can reduce the number of unnecessary casts. - */ - export function type(value: unknown): asserts value is T; - export function type(_value: unknown) { } - - export function getFunctionName(func: AnyFunction) { - if (typeof func !== "function") { - return ""; - } - else if (hasProperty(func, "name")) { - return (func as any).name; - } - else { - const text = Function.prototype.toString.call(func); - const match = /^function\s+([\w\$]+)\s*\(/.exec(text); - return match ? match[1] : ""; - } +/** @internal */ +export let assertMissingNode: { + // eslint-disable-next-line @typescript-eslint/prefer-function-type + (node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction): asserts node is undefined; +} = (node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction) => { + if (shouldAssertFunction(AssertionLevel.Normal, "assertMissingNode")) { + assert( + node === undefined, + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node!.kind)} was unexpected'.`, + stackCrawlMark || assertMissingNode); } +}; + +interface AssertionCacheEntry { + readonly disable: (level: AssertionLevel) => void, + readonly enable: (level: AssertionLevel) => void; +} + +function createAssertionCacheEntry(original: T, set: (fn: T) => void): AssertionCacheEntry { + let currentLevel: AssertionLevel | undefined; + return { + disable(level) { + set(noop as AnyFunction as T); + currentLevel = level; + }, + enable(level) { + if (currentLevel !== undefined && level >= currentLevel) { + set(original); + currentLevel = undefined; + } + }, + }; +} + +const assertionCache = { + assertEachNode: createAssertionCacheEntry(assertEachNode, fn => assertEachNode = fn), + assertNode: createAssertionCacheEntry(assertNode, fn => assertNode = fn), + assertNotNode: createAssertionCacheEntry(assertNotNode, fn => assertNotNode = fn), + assertOptionalNode: createAssertionCacheEntry(assertOptionalNode, fn => assertOptionalNode = fn), + assertOptionalToken: createAssertionCacheEntry(assertOptionalToken, fn => assertOptionalToken = fn), + assertMissingNode: createAssertionCacheEntry(assertMissingNode, fn => assertMissingNode = fn), +} as const; + +type AssertionKeys = keyof typeof assertionCache; + +/** + * Asserts a value has the specified type in typespace only (does not perform a runtime assertion). + * This is useful in cases where we switch on `node.kind` and can be reasonably sure the type is accurate, and + * as a result can reduce the number of unnecessary casts. + * + * @internal + */ +export function assertType(value: unknown): asserts value is T; +export function assertType(_value: unknown) { } - export function formatSymbol(symbol: Symbol): string { - return `{ name: ${unescapeLeadingUnderscores(symbol.escapedName)}; flags: ${formatSymbolFlags(symbol.flags)}; declarations: ${map(symbol.declarations, node => formatSyntaxKind(node.kind))} }`; +/** @internal */ +export function getFunctionName(func: AnyFunction) { + if (typeof func !== "function") { + return ""; } + else if (hasProperty(func, "name")) { + return (func as any).name; + } + else { + const text = Function.prototype.toString.call(func); + const match = /^function\s+([\w\$]+)\s*\(/.exec(text); + return match ? match[1] : ""; + } +} - /** - * Formats an enum value as a string for debugging and debug assertions. - */ - export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) { - const members = getEnumMembers(enumObject); - if (value === 0) { - return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0"; - } - if (isFlags) { - const result: string[] = []; - let remainingFlags = value; - for (const [enumValue, enumName] of members) { - if (enumValue > value) { - break; - } - if (enumValue !== 0 && enumValue & value) { - result.push(enumName); - remainingFlags &= ~enumValue; - } +/** @internal */ +export function formatSymbol(symbol: Symbol): string { + return `{ name: ${unescapeLeadingUnderscores(symbol.escapedName)}; flags: ${formatSymbolFlags(symbol.flags)}; declarations: ${map(symbol.declarations, node => formatSyntaxKind(node.kind))} }`; +} + +/** + * Formats an enum value as a string for debugging and debug assertions. + * + * @internal + */ +export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) { + const members = getEnumMembers(enumObject); + if (value === 0) { + return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0"; + } + if (isFlags) { + const result: string[] = []; + let remainingFlags = value; + for (const [enumValue, enumName] of members) { + if (enumValue > value) { + break; } - if (remainingFlags === 0) { - return result.join("|"); + if (enumValue !== 0 && enumValue & value) { + result.push(enumName); + remainingFlags &= ~enumValue; } } - else { - for (const [enumValue, enumName] of members) { - if (enumValue === value) { - return enumName; - } - } + if (remainingFlags === 0) { + return result.join("|"); } - return value.toString(); } - - const enumMemberCache = new Map>(); - - function getEnumMembers(enumObject: any) { - // Assuming enum objects do not change at runtime, we can cache the enum members list - // to reuse later. This saves us from reconstructing this each and every time we call - // a formatting function (which can be expensive for large enums like SyntaxKind). - const existing = enumMemberCache.get(enumObject); - if (existing) { - return existing; - } - - const result: [number, string][] = []; - for (const name in enumObject) { - const value = enumObject[name]; - if (typeof value === "number") { - result.push([value, name]); + else { + for (const [enumValue, enumName] of members) { + if (enumValue === value) { + return enumName; } } - - const sorted = stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0])); - enumMemberCache.set(enumObject, sorted); - return sorted; } + return value.toString(); +} - export function formatSyntaxKind(kind: SyntaxKind | undefined): string { - return formatEnum(kind, (ts as any).SyntaxKind, /*isFlags*/ false); - } +const enumMemberCache = new Map>(); - export function formatSnippetKind(kind: SnippetKind | undefined): string { - return formatEnum(kind, (ts as any).SnippetKind, /*isFlags*/ false); +function getEnumMembers(enumObject: any) { + // Assuming enum objects do not change at runtime, we can cache the enum members list + // to reuse later. This saves us from reconstructing this each and every time we call + // a formatting function (which can be expensive for large enums like SyntaxKind). + const existing = enumMemberCache.get(enumObject); + if (existing) { + return existing; } - export function formatNodeFlags(flags: NodeFlags | undefined): string { - return formatEnum(flags, (ts as any).NodeFlags, /*isFlags*/ true); + const result: [number, string][] = []; + for (const name in enumObject) { + const value = enumObject[name]; + if (typeof value === "number") { + result.push([value, name]); + } } - export function formatModifierFlags(flags: ModifierFlags | undefined): string { - return formatEnum(flags, (ts as any).ModifierFlags, /*isFlags*/ true); - } + const sorted = stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0])); + enumMemberCache.set(enumObject, sorted); + return sorted; +} - export function formatTransformFlags(flags: TransformFlags | undefined): string { - return formatEnum(flags, (ts as any).TransformFlags, /*isFlags*/ true); - } +/** @internal */ +export function formatSyntaxKind(kind: SyntaxKind | undefined): string { + return formatEnum(kind, (types as any).SyntaxKind, /*isFlags*/ false); +} - export function formatEmitFlags(flags: EmitFlags | undefined): string { - return formatEnum(flags, (ts as any).EmitFlags, /*isFlags*/ true); - } +/** @internal */ +export function formatSnippetKind(kind: SnippetKind | undefined): string { + return formatEnum(kind, (types as any).SnippetKind, /*isFlags*/ false); +} - export function formatSymbolFlags(flags: SymbolFlags | undefined): string { - return formatEnum(flags, (ts as any).SymbolFlags, /*isFlags*/ true); - } +/** @internal */ +export function formatNodeFlags(flags: NodeFlags | undefined): string { + return formatEnum(flags, (types as any).NodeFlags, /*isFlags*/ true); +} - export function formatTypeFlags(flags: TypeFlags | undefined): string { - return formatEnum(flags, (ts as any).TypeFlags, /*isFlags*/ true); - } +/** @internal */ +export function formatModifierFlags(flags: ModifierFlags | undefined): string { + return formatEnum(flags, (types as any).ModifierFlags, /*isFlags*/ true); +} - export function formatSignatureFlags(flags: SignatureFlags | undefined): string { - return formatEnum(flags, (ts as any).SignatureFlags, /*isFlags*/ true); - } +/** @internal */ +export function formatTransformFlags(flags: TransformFlags | undefined): string { + return formatEnum(flags, (types as any).TransformFlags, /*isFlags*/ true); +} - export function formatObjectFlags(flags: ObjectFlags | undefined): string { - return formatEnum(flags, (ts as any).ObjectFlags, /*isFlags*/ true); - } +/** @internal */ +export function formatEmitFlags(flags: EmitFlags | undefined): string { + return formatEnum(flags, (types as any).EmitFlags, /*isFlags*/ true); +} - export function formatFlowFlags(flags: FlowFlags | undefined): string { - return formatEnum(flags, (ts as any).FlowFlags, /*isFlags*/ true); - } +/** @internal */ +export function formatSymbolFlags(flags: SymbolFlags | undefined): string { + return formatEnum(flags, (types as any).SymbolFlags, /*isFlags*/ true); +} - export function formatRelationComparisonResult(result: RelationComparisonResult | undefined): string { - return formatEnum(result, (ts as any).RelationComparisonResult, /*isFlags*/ true); - } +/** @internal */ +export function formatTypeFlags(flags: TypeFlags | undefined): string { + return formatEnum(flags, (types as any).TypeFlags, /*isFlags*/ true); +} - export function formatCheckMode(mode: CheckMode | undefined): string { - return formatEnum(mode, (ts as any).CheckMode, /*isFlags*/ true); - } +/** @internal */ +export function formatSignatureFlags(flags: SignatureFlags | undefined): string { + return formatEnum(flags, (types as any).SignatureFlags, /*isFlags*/ true); +} - export function formatSignatureCheckMode(mode: SignatureCheckMode | undefined): string { - return formatEnum(mode, (ts as any).SignatureCheckMode, /*isFlags*/ true); - } +/** @internal */ +export function formatObjectFlags(flags: ObjectFlags | undefined): string { + return formatEnum(flags, (types as any).ObjectFlags, /*isFlags*/ true); +} - export function formatTypeFacts(facts: TypeFacts | undefined): string { - return formatEnum(facts, (ts as any).TypeFacts, /*isFlags*/ true); - } +/** @internal */ +export function formatFlowFlags(flags: FlowFlags | undefined): string { + return formatEnum(flags, (types as any).FlowFlags, /*isFlags*/ true); +} - let isDebugInfoEnabled = false; +/** @internal */ +export function formatRelationComparisonResult(result: RelationComparisonResult | undefined): string { + return formatEnum(result, (types as any).RelationComparisonResult, /*isFlags*/ true); +} - let flowNodeProto: FlowNodeBase | undefined; +/** @internal */ +export function formatCheckMode(mode: CheckMode | undefined): string { + return formatEnum(mode, (types as any).CheckMode, /*isFlags*/ true); +} - function attachFlowNodeDebugInfoWorker(flowNode: FlowNodeBase) { - if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line local/no-in-operator - Object.defineProperties(flowNode, { - // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { - value(this: FlowNodeBase) { - const flowHeader = - this.flags & FlowFlags.Start ? "FlowStart" : - this.flags & FlowFlags.BranchLabel ? "FlowBranchLabel" : - this.flags & FlowFlags.LoopLabel ? "FlowLoopLabel" : - this.flags & FlowFlags.Assignment ? "FlowAssignment" : - this.flags & FlowFlags.TrueCondition ? "FlowTrueCondition" : - this.flags & FlowFlags.FalseCondition ? "FlowFalseCondition" : - this.flags & FlowFlags.SwitchClause ? "FlowSwitchClause" : - this.flags & FlowFlags.ArrayMutation ? "FlowArrayMutation" : - this.flags & FlowFlags.Call ? "FlowCall" : - this.flags & FlowFlags.ReduceLabel ? "FlowReduceLabel" : - this.flags & FlowFlags.Unreachable ? "FlowUnreachable" : - "UnknownFlow"; - const remainingFlags = this.flags & ~(FlowFlags.Referenced - 1); - return `${flowHeader}${remainingFlags ? ` (${formatFlowFlags(remainingFlags)})`: ""}`; - } - }, - __debugFlowFlags: { get(this: FlowNodeBase) { return formatEnum(this.flags, (ts as any).FlowFlags, /*isFlags*/ true); } }, - __debugToString: { value(this: FlowNodeBase) { return formatControlFlowGraph(this); } } - }); - } - } +/** @internal */ +export function formatSignatureCheckMode(mode: SignatureCheckMode | undefined): string { + return formatEnum(mode, (types as any).SignatureCheckMode, /*isFlags*/ true); +} - export function attachFlowNodeDebugInfo(flowNode: FlowNodeBase) { - if (isDebugInfoEnabled) { - if (typeof Object.setPrototypeOf === "function") { - // if we're in es2015, attach the method to a shared prototype for `FlowNode` - // so the method doesn't show up in the watch window. - if (!flowNodeProto) { - flowNodeProto = Object.create(Object.prototype) as FlowNodeBase; - attachFlowNodeDebugInfoWorker(flowNodeProto); - } - Object.setPrototypeOf(flowNode, flowNodeProto); - } - else { - // not running in an es2015 environment, attach the method directly. - attachFlowNodeDebugInfoWorker(flowNode); - } - } - } +/** @internal */ +export function formatTypeFacts(facts: TypeFacts | undefined): string { + return formatEnum(facts, (types as any).TypeFacts, /*isFlags*/ true); +} - let nodeArrayProto: NodeArray | undefined; +let isDebugInfoEnabled = false; - function attachNodeArrayDebugInfoWorker(array: NodeArray) { - if (!("__tsDebuggerDisplay" in array)) { // eslint-disable-line local/no-in-operator - Object.defineProperties(array, { - __tsDebuggerDisplay: { - value(this: NodeArray, defaultValue: string) { - // An `Array` with extra properties is rendered as `[A, B, prop1: 1, prop2: 2]`. Most of - // these aren't immediately useful so we trim off the `prop1: ..., prop2: ...` part from the - // formatted string. - // This regex can trigger slow backtracking because of overlapping potential captures. - // We don't care, this is debug code that's only enabled with a debugger attached - - // we're just taking note of it for anyone checking regex performance in the future. - defaultValue = String(defaultValue).replace(/(?:,[\s\w\d_]+:[^,]+)+\]$/, "]"); - return `NodeArray ${defaultValue}`; - } +let flowNodeProto: FlowNodeBase | undefined; + +function attachFlowNodeDebugInfoWorker(flowNode: FlowNodeBase) { + if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line local/no-in-operator + Object.defineProperties(flowNode, { + // for use with vscode-js-debug's new customDescriptionGenerator in launch.json + __tsDebuggerDisplay: { + value(this: FlowNodeBase) { + const flowHeader = + this.flags & FlowFlags.Start ? "FlowStart" : + this.flags & FlowFlags.BranchLabel ? "FlowBranchLabel" : + this.flags & FlowFlags.LoopLabel ? "FlowLoopLabel" : + this.flags & FlowFlags.Assignment ? "FlowAssignment" : + this.flags & FlowFlags.TrueCondition ? "FlowTrueCondition" : + this.flags & FlowFlags.FalseCondition ? "FlowFalseCondition" : + this.flags & FlowFlags.SwitchClause ? "FlowSwitchClause" : + this.flags & FlowFlags.ArrayMutation ? "FlowArrayMutation" : + this.flags & FlowFlags.Call ? "FlowCall" : + this.flags & FlowFlags.ReduceLabel ? "FlowReduceLabel" : + this.flags & FlowFlags.Unreachable ? "FlowUnreachable" : + "UnknownFlow"; + const remainingFlags = this.flags & ~(FlowFlags.Referenced - 1); + return `${flowHeader}${remainingFlags ? ` (${formatFlowFlags(remainingFlags)})`: ""}`; } - }); - } + }, + __debugFlowFlags: { get(this: FlowNodeBase) { return formatEnum(this.flags, (types as any).FlowFlags, /*isFlags*/ true); } }, + __debugToString: { value(this: FlowNodeBase) { return formatControlFlowGraph(this); } } + }); } +} - export function attachNodeArrayDebugInfo(array: NodeArray) { - if (isDebugInfoEnabled) { - if (typeof Object.setPrototypeOf === "function") { - // if we're in es2015, attach the method to a shared prototype for `NodeArray` - // so the method doesn't show up in the watch window. - if (!nodeArrayProto) { - nodeArrayProto = Object.create(Array.prototype) as NodeArray; - attachNodeArrayDebugInfoWorker(nodeArrayProto); - } - Object.setPrototypeOf(array, nodeArrayProto); - } - else { - // not running in an es2015 environment, attach the method directly. - attachNodeArrayDebugInfoWorker(array); +/** @internal */ +export function attachFlowNodeDebugInfo(flowNode: FlowNodeBase) { + if (isDebugInfoEnabled) { + if (typeof Object.setPrototypeOf === "function") { + // if we're in es2015, attach the method to a shared prototype for `FlowNode` + // so the method doesn't show up in the watch window. + if (!flowNodeProto) { + flowNodeProto = Object.create(Object.prototype) as FlowNodeBase; + attachFlowNodeDebugInfoWorker(flowNodeProto); } + Object.setPrototypeOf(flowNode, flowNodeProto); + } + else { + // not running in an es2015 environment, attach the method directly. + attachFlowNodeDebugInfoWorker(flowNode); } } +} - /** - * Injects debug information into frequently used types. - */ - export function enableDebugInfo() { - if (isDebugInfoEnabled) return; - - // avoid recomputing - const weakTypeTextMap = new WeakMap(); - const weakNodeTextMap = new WeakMap(); +let nodeArrayProto: NodeArray | undefined; - // Add additional properties in debug mode to assist with debugging. - Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, { - // for use with vscode-js-debug's new customDescriptionGenerator in launch.json +function attachNodeArrayDebugInfoWorker(array: NodeArray) { + if (!("__tsDebuggerDisplay" in array)) { // eslint-disable-line local/no-in-operator + Object.defineProperties(array, { __tsDebuggerDisplay: { - value(this: Symbol) { - const symbolHeader = - this.flags & SymbolFlags.Transient ? "TransientSymbol" : - "Symbol"; - const remainingSymbolFlags = this.flags & ~SymbolFlags.Transient; - return `${symbolHeader} '${symbolName(this)}'${remainingSymbolFlags ? ` (${formatSymbolFlags(remainingSymbolFlags)})` : ""}`; + value(this: NodeArray, defaultValue: string) { + // An `Array` with extra properties is rendered as `[A, B, prop1: 1, prop2: 2]`. Most of + // these aren't immediately useful so we trim off the `prop1: ..., prop2: ...` part from the + // formatted string. + // This regex can trigger slow backtracking because of overlapping potential captures. + // We don't care, this is debug code that's only enabled with a debugger attached - + // we're just taking note of it for anyone checking regex performance in the future. + defaultValue = String(defaultValue).replace(/(?:,[\s\w\d_]+:[^,]+)+\]$/, "]"); + return `NodeArray ${defaultValue}`; } - }, - __debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } } + } }); + } +} - Object.defineProperties(objectAllocator.getTypeConstructor().prototype, { - // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { - value(this: Type) { - const typeHeader = - this.flags & TypeFlags.Nullable ? "NullableType" : - this.flags & TypeFlags.StringOrNumberLiteral ? `LiteralType ${JSON.stringify((this as LiteralType).value)}` : - this.flags & TypeFlags.BigIntLiteral ? `LiteralType ${(this as BigIntLiteralType).value.negative ? "-" : ""}${(this as BigIntLiteralType).value.base10Value}n` : - this.flags & TypeFlags.UniqueESSymbol ? "UniqueESSymbolType" : - this.flags & TypeFlags.Enum ? "EnumType" : - this.flags & TypeFlags.Intrinsic ? `IntrinsicType ${(this as IntrinsicType).intrinsicName}` : - this.flags & TypeFlags.Union ? "UnionType" : - this.flags & TypeFlags.Intersection ? "IntersectionType" : - this.flags & TypeFlags.Index ? "IndexType" : - this.flags & TypeFlags.IndexedAccess ? "IndexedAccessType" : - this.flags & TypeFlags.Conditional ? "ConditionalType" : - this.flags & TypeFlags.Substitution ? "SubstitutionType" : - this.flags & TypeFlags.TypeParameter ? "TypeParameter" : - this.flags & TypeFlags.Object ? - (this as ObjectType).objectFlags & ObjectFlags.ClassOrInterface ? "InterfaceType" : - (this as ObjectType).objectFlags & ObjectFlags.Reference ? "TypeReference" : - (this as ObjectType).objectFlags & ObjectFlags.Tuple ? "TupleType" : - (this as ObjectType).objectFlags & ObjectFlags.Anonymous ? "AnonymousType" : - (this as ObjectType).objectFlags & ObjectFlags.Mapped ? "MappedType" : - (this as ObjectType).objectFlags & ObjectFlags.ReverseMapped ? "ReverseMappedType" : - (this as ObjectType).objectFlags & ObjectFlags.EvolvingArray ? "EvolvingArrayType" : - "ObjectType" : - "Type"; - const remainingObjectFlags = this.flags & TypeFlags.Object ? (this as ObjectType).objectFlags & ~ObjectFlags.ObjectTypeKindMask : 0; - return `${typeHeader}${this.symbol ? ` '${symbolName(this.symbol)}'` : ""}${remainingObjectFlags ? ` (${formatObjectFlags(remainingObjectFlags)})` : ""}`; +/** @internal */ +export function attachNodeArrayDebugInfo(array: NodeArray) { + if (isDebugInfoEnabled) { + if (typeof Object.setPrototypeOf === "function") { + // if we're in es2015, attach the method to a shared prototype for `NodeArray` + // so the method doesn't show up in the watch window. + if (!nodeArrayProto) { + nodeArrayProto = Object.create(Array.prototype) as NodeArray; + attachNodeArrayDebugInfoWorker(nodeArrayProto); + } + Object.setPrototypeOf(array, nodeArrayProto); + } + else { + // not running in an es2015 environment, attach the method directly. + attachNodeArrayDebugInfoWorker(array); + } + } +} + +/** + * Injects debug information into frequently used types. + * + * @internal + */ +export function enableDebugInfo() { + if (isDebugInfoEnabled) return; + + // avoid recomputing + const weakTypeTextMap = new WeakMap(); + const weakNodeTextMap = new WeakMap(); + + // Add additional properties in debug mode to assist with debugging. + Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, { + // for use with vscode-js-debug's new customDescriptionGenerator in launch.json + __tsDebuggerDisplay: { + value(this: Symbol) { + const symbolHeader = + this.flags & SymbolFlags.Transient ? "TransientSymbol" : + "Symbol"; + const remainingSymbolFlags = this.flags & ~SymbolFlags.Transient; + return `${symbolHeader} '${symbolName(this)}'${remainingSymbolFlags ? ` (${formatSymbolFlags(remainingSymbolFlags)})` : ""}`; + } + }, + __debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } } + }); + + Object.defineProperties(objectAllocator.getTypeConstructor().prototype, { + // for use with vscode-js-debug's new customDescriptionGenerator in launch.json + __tsDebuggerDisplay: { + value(this: Type) { + const typeHeader = + this.flags & TypeFlags.Nullable ? "NullableType" : + this.flags & TypeFlags.StringOrNumberLiteral ? `LiteralType ${JSON.stringify((this as LiteralType).value)}` : + this.flags & TypeFlags.BigIntLiteral ? `LiteralType ${(this as BigIntLiteralType).value.negative ? "-" : ""}${(this as BigIntLiteralType).value.base10Value}n` : + this.flags & TypeFlags.UniqueESSymbol ? "UniqueESSymbolType" : + this.flags & TypeFlags.Enum ? "EnumType" : + this.flags & TypeFlags.Intrinsic ? `IntrinsicType ${(this as IntrinsicType).intrinsicName}` : + this.flags & TypeFlags.Union ? "UnionType" : + this.flags & TypeFlags.Intersection ? "IntersectionType" : + this.flags & TypeFlags.Index ? "IndexType" : + this.flags & TypeFlags.IndexedAccess ? "IndexedAccessType" : + this.flags & TypeFlags.Conditional ? "ConditionalType" : + this.flags & TypeFlags.Substitution ? "SubstitutionType" : + this.flags & TypeFlags.TypeParameter ? "TypeParameter" : + this.flags & TypeFlags.Object ? + (this as ObjectType).objectFlags & ObjectFlags.ClassOrInterface ? "InterfaceType" : + (this as ObjectType).objectFlags & ObjectFlags.Reference ? "TypeReference" : + (this as ObjectType).objectFlags & ObjectFlags.Tuple ? "TupleType" : + (this as ObjectType).objectFlags & ObjectFlags.Anonymous ? "AnonymousType" : + (this as ObjectType).objectFlags & ObjectFlags.Mapped ? "MappedType" : + (this as ObjectType).objectFlags & ObjectFlags.ReverseMapped ? "ReverseMappedType" : + (this as ObjectType).objectFlags & ObjectFlags.EvolvingArray ? "EvolvingArrayType" : + "ObjectType" : + "Type"; + const remainingObjectFlags = this.flags & TypeFlags.Object ? (this as ObjectType).objectFlags & ~ObjectFlags.ObjectTypeKindMask : 0; + return `${typeHeader}${this.symbol ? ` '${symbolName(this.symbol)}'` : ""}${remainingObjectFlags ? ` (${formatObjectFlags(remainingObjectFlags)})` : ""}`; + } + }, + __debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } }, + __debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((this as ObjectType).objectFlags) : ""; } }, + __debugTypeToString: { + value(this: Type) { + // avoid recomputing + let text = weakTypeTextMap.get(this); + if (text === undefined) { + text = this.checker.typeToString(this); + weakTypeTextMap.set(this, text); } - }, - __debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } }, - __debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((this as ObjectType).objectFlags) : ""; } }, - __debugTypeToString: { - value(this: Type) { - // avoid recomputing - let text = weakTypeTextMap.get(this); - if (text === undefined) { - text = this.checker.typeToString(this); - weakTypeTextMap.set(this, text); + return text; + } + }, + }); + + Object.defineProperties(objectAllocator.getSignatureConstructor().prototype, { + __debugFlags: { get(this: Signature) { return formatSignatureFlags(this.flags); } }, + __debugSignatureToString: { value(this: Signature) { return this.checker?.signatureToString(this); } } + }); + + const nodeConstructors = [ + objectAllocator.getNodeConstructor(), + objectAllocator.getIdentifierConstructor(), + objectAllocator.getTokenConstructor(), + objectAllocator.getSourceFileConstructor() + ]; + + for (const ctor of nodeConstructors) { + if (!hasProperty(ctor.prototype, "__debugKind")) { + Object.defineProperties(ctor.prototype, { + // for use with vscode-js-debug's new customDescriptionGenerator in launch.json + __tsDebuggerDisplay: { + value(this: Node) { + const nodeHeader = + isGeneratedIdentifier(this) ? "GeneratedIdentifier" : + isIdentifier(this) ? `Identifier '${idText(this)}'` : + isPrivateIdentifier(this) ? `PrivateIdentifier '${idText(this)}'` : + isStringLiteral(this) ? `StringLiteral ${JSON.stringify(this.text.length < 10 ? this.text : this.text.slice(10) + "...")}` : + isNumericLiteral(this) ? `NumericLiteral ${this.text}` : + isBigIntLiteral(this) ? `BigIntLiteral ${this.text}n` : + isTypeParameterDeclaration(this) ? "TypeParameterDeclaration" : + isParameter(this) ? "ParameterDeclaration" : + isConstructorDeclaration(this) ? "ConstructorDeclaration" : + isGetAccessorDeclaration(this) ? "GetAccessorDeclaration" : + isSetAccessorDeclaration(this) ? "SetAccessorDeclaration" : + isCallSignatureDeclaration(this) ? "CallSignatureDeclaration" : + isConstructSignatureDeclaration(this) ? "ConstructSignatureDeclaration" : + isIndexSignatureDeclaration(this) ? "IndexSignatureDeclaration" : + isTypePredicateNode(this) ? "TypePredicateNode" : + isTypeReferenceNode(this) ? "TypeReferenceNode" : + isFunctionTypeNode(this) ? "FunctionTypeNode" : + isConstructorTypeNode(this) ? "ConstructorTypeNode" : + isTypeQueryNode(this) ? "TypeQueryNode" : + isTypeLiteralNode(this) ? "TypeLiteralNode" : + isArrayTypeNode(this) ? "ArrayTypeNode" : + isTupleTypeNode(this) ? "TupleTypeNode" : + isOptionalTypeNode(this) ? "OptionalTypeNode" : + isRestTypeNode(this) ? "RestTypeNode" : + isUnionTypeNode(this) ? "UnionTypeNode" : + isIntersectionTypeNode(this) ? "IntersectionTypeNode" : + isConditionalTypeNode(this) ? "ConditionalTypeNode" : + isInferTypeNode(this) ? "InferTypeNode" : + isParenthesizedTypeNode(this) ? "ParenthesizedTypeNode" : + isThisTypeNode(this) ? "ThisTypeNode" : + isTypeOperatorNode(this) ? "TypeOperatorNode" : + isIndexedAccessTypeNode(this) ? "IndexedAccessTypeNode" : + isMappedTypeNode(this) ? "MappedTypeNode" : + isLiteralTypeNode(this) ? "LiteralTypeNode" : + isNamedTupleMember(this) ? "NamedTupleMember" : + isImportTypeNode(this) ? "ImportTypeNode" : + formatSyntaxKind(this.kind); + return `${nodeHeader}${this.flags ? ` (${formatNodeFlags(this.flags)})` : ""}`; } - return text; - } - }, - }); - - Object.defineProperties(objectAllocator.getSignatureConstructor().prototype, { - __debugFlags: { get(this: Signature) { return formatSignatureFlags(this.flags); } }, - __debugSignatureToString: { value(this: Signature) { return this.checker?.signatureToString(this); } } - }); - - const nodeConstructors = [ - objectAllocator.getNodeConstructor(), - objectAllocator.getIdentifierConstructor(), - objectAllocator.getTokenConstructor(), - objectAllocator.getSourceFileConstructor() - ]; - - for (const ctor of nodeConstructors) { - if (!hasProperty(ctor.prototype, "__debugKind")) { - Object.defineProperties(ctor.prototype, { - // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { - value(this: Node) { - const nodeHeader = - isGeneratedIdentifier(this) ? "GeneratedIdentifier" : - isIdentifier(this) ? `Identifier '${idText(this)}'` : - isPrivateIdentifier(this) ? `PrivateIdentifier '${idText(this)}'` : - isStringLiteral(this) ? `StringLiteral ${JSON.stringify(this.text.length < 10 ? this.text : this.text.slice(10) + "...")}` : - isNumericLiteral(this) ? `NumericLiteral ${this.text}` : - isBigIntLiteral(this) ? `BigIntLiteral ${this.text}n` : - isTypeParameterDeclaration(this) ? "TypeParameterDeclaration" : - isParameter(this) ? "ParameterDeclaration" : - isConstructorDeclaration(this) ? "ConstructorDeclaration" : - isGetAccessorDeclaration(this) ? "GetAccessorDeclaration" : - isSetAccessorDeclaration(this) ? "SetAccessorDeclaration" : - isCallSignatureDeclaration(this) ? "CallSignatureDeclaration" : - isConstructSignatureDeclaration(this) ? "ConstructSignatureDeclaration" : - isIndexSignatureDeclaration(this) ? "IndexSignatureDeclaration" : - isTypePredicateNode(this) ? "TypePredicateNode" : - isTypeReferenceNode(this) ? "TypeReferenceNode" : - isFunctionTypeNode(this) ? "FunctionTypeNode" : - isConstructorTypeNode(this) ? "ConstructorTypeNode" : - isTypeQueryNode(this) ? "TypeQueryNode" : - isTypeLiteralNode(this) ? "TypeLiteralNode" : - isArrayTypeNode(this) ? "ArrayTypeNode" : - isTupleTypeNode(this) ? "TupleTypeNode" : - isOptionalTypeNode(this) ? "OptionalTypeNode" : - isRestTypeNode(this) ? "RestTypeNode" : - isUnionTypeNode(this) ? "UnionTypeNode" : - isIntersectionTypeNode(this) ? "IntersectionTypeNode" : - isConditionalTypeNode(this) ? "ConditionalTypeNode" : - isInferTypeNode(this) ? "InferTypeNode" : - isParenthesizedTypeNode(this) ? "ParenthesizedTypeNode" : - isThisTypeNode(this) ? "ThisTypeNode" : - isTypeOperatorNode(this) ? "TypeOperatorNode" : - isIndexedAccessTypeNode(this) ? "IndexedAccessTypeNode" : - isMappedTypeNode(this) ? "MappedTypeNode" : - isLiteralTypeNode(this) ? "LiteralTypeNode" : - isNamedTupleMember(this) ? "NamedTupleMember" : - isImportTypeNode(this) ? "ImportTypeNode" : - formatSyntaxKind(this.kind); - return `${nodeHeader}${this.flags ? ` (${formatNodeFlags(this.flags)})` : ""}`; - } - }, - __debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } }, - __debugNodeFlags: { get(this: Node) { return formatNodeFlags(this.flags); } }, - __debugModifierFlags: { get(this: Node) { return formatModifierFlags(getEffectiveModifierFlagsNoCache(this)); } }, - __debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } }, - __debugIsParseTreeNode: { get(this: Node) { return isParseTreeNode(this); } }, - __debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } }, - __debugGetText: { - value(this: Node, includeTrivia?: boolean) { - if (nodeIsSynthesized(this)) return ""; - // avoid recomputing - let text = weakNodeTextMap.get(this); - if (text === undefined) { - const parseNode = getParseTreeNode(this); - const sourceFile = parseNode && getSourceFileOfNode(parseNode); - text = sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : ""; - weakNodeTextMap.set(this, text); - } - return text; + }, + __debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } }, + __debugNodeFlags: { get(this: Node) { return formatNodeFlags(this.flags); } }, + __debugModifierFlags: { get(this: Node) { return formatModifierFlags(getEffectiveModifierFlagsNoCache(this)); } }, + __debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } }, + __debugIsParseTreeNode: { get(this: Node) { return isParseTreeNode(this); } }, + __debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } }, + __debugGetText: { + value(this: Node, includeTrivia?: boolean) { + if (nodeIsSynthesized(this)) return ""; + // avoid recomputing + let text = weakNodeTextMap.get(this); + if (text === undefined) { + const parseNode = getParseTreeNode(this); + const sourceFile = parseNode && getSourceFileOfNode(parseNode); + text = sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : ""; + weakNodeTextMap.set(this, text); } + return text; } - }); - } + } + }); } - - isDebugInfoEnabled = true; } - export function formatVariance(varianceFlags: VarianceFlags) { - const variance = varianceFlags & VarianceFlags.VarianceMask; - let result = - variance === VarianceFlags.Invariant ? "in out" : - variance === VarianceFlags.Bivariant ? "[bivariant]" : - variance === VarianceFlags.Contravariant ? "in" : - variance === VarianceFlags.Covariant ? "out" : - variance === VarianceFlags.Independent ? "[independent]" : ""; - if (varianceFlags & VarianceFlags.Unmeasurable) { - result += " (unmeasurable)"; - } - else if (varianceFlags & VarianceFlags.Unreliable) { - result += " (unreliable)"; - } - return result; + isDebugInfoEnabled = true; +} + +/** @internal */ +export function formatVariance(varianceFlags: VarianceFlags) { + const variance = varianceFlags & VarianceFlags.VarianceMask; + let result = + variance === VarianceFlags.Invariant ? "in out" : + variance === VarianceFlags.Bivariant ? "[bivariant]" : + variance === VarianceFlags.Contravariant ? "in" : + variance === VarianceFlags.Covariant ? "out" : + variance === VarianceFlags.Independent ? "[independent]" : ""; + if (varianceFlags & VarianceFlags.Unmeasurable) { + result += " (unmeasurable)"; + } + else if (varianceFlags & VarianceFlags.Unreliable) { + result += " (unreliable)"; } + return result; +} - export type DebugType = Type & { __debugTypeToString(): string }; // eslint-disable-line @typescript-eslint/naming-convention - export class DebugTypeMapper { - declare kind: TypeMapKind; - __debugToString(): string { // eslint-disable-line @typescript-eslint/naming-convention - type(this); - switch (this.kind) { - case TypeMapKind.Function: return this.debugInfo?.() || "(function mapper)"; - case TypeMapKind.Simple: return `${(this.source as DebugType).__debugTypeToString()} -> ${(this.target as DebugType).__debugTypeToString()}`; - case TypeMapKind.Array: return zipWith( - this.sources as readonly DebugType[], - this.targets as readonly DebugType[] || map(this.sources, () => "any"), - (s, t) => `${s.__debugTypeToString()} -> ${typeof t === "string" ? t : t.__debugTypeToString()}`).join(", "); - case TypeMapKind.Deferred: return zipWith( - this.sources, - this.targets, - (s, t) => `${(s as DebugType).__debugTypeToString()} -> ${(t() as DebugType).__debugTypeToString()}`).join(", "); - case TypeMapKind.Merged: - case TypeMapKind.Composite: return `m1: ${(this.mapper1 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n ")} +/** @internal */ +export type DebugType = Type & { __debugTypeToString(): string }; // eslint-disable-line @typescript-eslint/naming-convention +/** @internal */ +export class DebugTypeMapper { + declare kind: TypeMapKind; + __debugToString(): string { // eslint-disable-line @typescript-eslint/naming-convention + assertType(this); + switch (this.kind) { + case TypeMapKind.Function: return this.debugInfo?.() || "(function mapper)"; + case TypeMapKind.Simple: return `${(this.source as DebugType).__debugTypeToString()} -> ${(this.target as DebugType).__debugTypeToString()}`; + case TypeMapKind.Array: return zipWith( + this.sources as readonly DebugType[], + this.targets as readonly DebugType[] || map(this.sources, () => "any"), + (s, t) => `${s.__debugTypeToString()} -> ${typeof t === "string" ? t : t.__debugTypeToString()}`).join(", "); + case TypeMapKind.Deferred: return zipWith( + this.sources, + this.targets, + (s, t) => `${(s as DebugType).__debugTypeToString()} -> ${(t() as DebugType).__debugTypeToString()}`).join(", "); + case TypeMapKind.Merged: + case TypeMapKind.Composite: return `m1: ${(this.mapper1 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n ")} m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n ")}`; - default: return assertNever(this); - } + default: return assertNever(this); } } +} - export function attachDebugPrototypeIfDebug(mapper: TypeMapper): TypeMapper { - if (isDebugging) { - return Object.setPrototypeOf(mapper, DebugTypeMapper.prototype); - } - return mapper; +/** @internal */ +export function attachDebugPrototypeIfDebug(mapper: TypeMapper): TypeMapper { + if (isDebugging) { + return Object.setPrototypeOf(mapper, DebugTypeMapper.prototype); } + return mapper; +} - export function printControlFlowGraph(flowNode: FlowNode) { - return console.log(formatControlFlowGraph(flowNode)); - } +/** @internal */ +export function printControlFlowGraph(flowNode: FlowNode) { + return console.log(formatControlFlowGraph(flowNode)); +} - export function formatControlFlowGraph(flowNode: FlowNode) { - let nextDebugFlowId = -1; +/** @internal */ +export function formatControlFlowGraph(flowNode: FlowNode) { + let nextDebugFlowId = -1; - function getDebugFlowNodeId(f: FlowNode) { - if (!f.id) { - f.id = nextDebugFlowId; - nextDebugFlowId--; - } - return f.id; + function getDebugFlowNodeId(f: FlowNode) { + if (!f.id) { + f.id = nextDebugFlowId; + nextDebugFlowId--; } + return f.id; + } - const enum BoxCharacter { - lr = "─", - ud = "│", - dr = "╭", - dl = "╮", - ul = "╯", - ur = "╰", - udr = "├", - udl = "┤", - dlr = "┬", - ulr = "┴", - udlr = "╫", - } + const enum BoxCharacter { + lr = "─", + ud = "│", + dr = "╭", + dl = "╮", + ul = "╯", + ur = "╰", + udr = "├", + udl = "┤", + dlr = "┬", + ulr = "┴", + udlr = "╫", + } - const enum Connection { - None = 0, - Up = 1 << 0, - Down = 1 << 1, - Left = 1 << 2, - Right = 1 << 3, - - UpDown = Up | Down, - LeftRight = Left | Right, - UpLeft = Up | Left, - UpRight = Up | Right, - DownLeft = Down | Left, - DownRight = Down | Right, - UpDownLeft = UpDown | Left, - UpDownRight = UpDown | Right, - UpLeftRight = Up | LeftRight, - DownLeftRight = Down | LeftRight, - UpDownLeftRight = UpDown | LeftRight, - - NoChildren = 1 << 4, - } + const enum Connection { + None = 0, + Up = 1 << 0, + Down = 1 << 1, + Left = 1 << 2, + Right = 1 << 3, + + UpDown = Up | Down, + LeftRight = Left | Right, + UpLeft = Up | Left, + UpRight = Up | Right, + DownLeft = Down | Left, + DownRight = Down | Right, + UpDownLeft = UpDown | Left, + UpDownRight = UpDown | Right, + UpLeftRight = Up | LeftRight, + DownLeftRight = Down | LeftRight, + UpDownLeftRight = UpDown | LeftRight, + + NoChildren = 1 << 4, + } - interface FlowGraphNode { - id: number; - flowNode: FlowNode; - edges: FlowGraphEdge[]; - text: string; - lane: number; - endLane: number; - level: number; - circular: boolean | "circularity"; - } + interface FlowGraphNode { + id: number; + flowNode: FlowNode; + edges: FlowGraphEdge[]; + text: string; + lane: number; + endLane: number; + level: number; + circular: boolean | "circularity"; + } - interface FlowGraphEdge { - source: FlowGraphNode; - target: FlowGraphNode; - } + interface FlowGraphEdge { + source: FlowGraphNode; + target: FlowGraphNode; + } - const hasAntecedentFlags = - FlowFlags.Assignment | - FlowFlags.Condition | - FlowFlags.SwitchClause | - FlowFlags.ArrayMutation | - FlowFlags.Call | - FlowFlags.ReduceLabel; - - const hasNodeFlags = - FlowFlags.Start | - FlowFlags.Assignment | - FlowFlags.Call | - FlowFlags.Condition | - FlowFlags.ArrayMutation; - - const links: Record = Object.create(/*o*/ null); // eslint-disable-line no-null/no-null - const nodes: FlowGraphNode[] = []; - const edges: FlowGraphEdge[] = []; - const root = buildGraphNode(flowNode, new Set()); - for (const node of nodes) { - node.text = renderFlowNode(node.flowNode, node.circular); - computeLevel(node); - } + const hasAntecedentFlags = + FlowFlags.Assignment | + FlowFlags.Condition | + FlowFlags.SwitchClause | + FlowFlags.ArrayMutation | + FlowFlags.Call | + FlowFlags.ReduceLabel; + + const hasNodeFlags = + FlowFlags.Start | + FlowFlags.Assignment | + FlowFlags.Call | + FlowFlags.Condition | + FlowFlags.ArrayMutation; + + const links: Record = Object.create(/*o*/ null); // eslint-disable-line no-null/no-null + const nodes: FlowGraphNode[] = []; + const edges: FlowGraphEdge[] = []; + const root = buildGraphNode(flowNode, new Set()); + for (const node of nodes) { + node.text = renderFlowNode(node.flowNode, node.circular); + computeLevel(node); + } - const height = computeHeight(root); - const columnWidths = computeColumnWidths(height); - computeLanes(root, 0); - return renderGraph(); + const height = computeHeight(root); + const columnWidths = computeColumnWidths(height); + computeLanes(root, 0); + return renderGraph(); - function isFlowSwitchClause(f: FlowNode): f is FlowSwitchClause { - return !!(f.flags & FlowFlags.SwitchClause); - } + function isFlowSwitchClause(f: FlowNode): f is FlowSwitchClause { + return !!(f.flags & FlowFlags.SwitchClause); + } - function hasAntecedents(f: FlowNode): f is FlowLabel & { antecedents: FlowNode[] } { - return !!(f.flags & FlowFlags.Label) && !!(f as FlowLabel).antecedents; - } + function hasAntecedents(f: FlowNode): f is FlowLabel & { antecedents: FlowNode[] } { + return !!(f.flags & FlowFlags.Label) && !!(f as FlowLabel).antecedents; + } - function hasAntecedent(f: FlowNode): f is Extract { - return !!(f.flags & hasAntecedentFlags); - } + function hasAntecedent(f: FlowNode): f is Extract { + return !!(f.flags & hasAntecedentFlags); + } - function hasNode(f: FlowNode): f is Extract { - return !!(f.flags & hasNodeFlags); - } + function hasNode(f: FlowNode): f is Extract { + return !!(f.flags & hasNodeFlags); + } - function getChildren(node: FlowGraphNode) { - const children: FlowGraphNode[] = []; - for (const edge of node.edges) { - if (edge.source === node) { - children.push(edge.target); - } + function getChildren(node: FlowGraphNode) { + const children: FlowGraphNode[] = []; + for (const edge of node.edges) { + if (edge.source === node) { + children.push(edge.target); } - return children; } + return children; + } - function getParents(node: FlowGraphNode) { - const parents: FlowGraphNode[] = []; - for (const edge of node.edges) { - if (edge.target === node) { - parents.push(edge.source); - } + function getParents(node: FlowGraphNode) { + const parents: FlowGraphNode[] = []; + for (const edge of node.edges) { + if (edge.target === node) { + parents.push(edge.source); } - return parents; } + return parents; + } - function buildGraphNode(flowNode: FlowNode, seen: Set): FlowGraphNode { - const id = getDebugFlowNodeId(flowNode); - let graphNode = links[id]; - if (graphNode && seen.has(flowNode)) { - graphNode.circular = true; - graphNode = { - id: -1, - flowNode, - edges: [], - text: "", - lane: -1, - endLane: -1, - level: -1, - circular: "circularity" - }; - nodes.push(graphNode); - return graphNode; - } - seen.add(flowNode); - if (!graphNode) { - links[id] = graphNode = { id, flowNode, edges: [], text: "", lane: -1, endLane: -1, level: -1, circular: false }; - nodes.push(graphNode); - if (hasAntecedents(flowNode)) { - for (const antecedent of flowNode.antecedents) { - buildGraphEdge(graphNode, antecedent, seen); - } - } - else if (hasAntecedent(flowNode)) { - buildGraphEdge(graphNode, flowNode.antecedent, seen); + function buildGraphNode(flowNode: FlowNode, seen: Set): FlowGraphNode { + const id = getDebugFlowNodeId(flowNode); + let graphNode = links[id]; + if (graphNode && seen.has(flowNode)) { + graphNode.circular = true; + graphNode = { + id: -1, + flowNode, + edges: [], + text: "", + lane: -1, + endLane: -1, + level: -1, + circular: "circularity" + }; + nodes.push(graphNode); + return graphNode; + } + seen.add(flowNode); + if (!graphNode) { + links[id] = graphNode = { id, flowNode, edges: [], text: "", lane: -1, endLane: -1, level: -1, circular: false }; + nodes.push(graphNode); + if (hasAntecedents(flowNode)) { + for (const antecedent of flowNode.antecedents) { + buildGraphEdge(graphNode, antecedent, seen); } } - seen.delete(flowNode); - return graphNode; + else if (hasAntecedent(flowNode)) { + buildGraphEdge(graphNode, flowNode.antecedent, seen); + } } + seen.delete(flowNode); + return graphNode; + } - function buildGraphEdge(source: FlowGraphNode, antecedent: FlowNode, seen: Set) { - const target = buildGraphNode(antecedent, seen); - const edge: FlowGraphEdge = { source, target }; - edges.push(edge); - source.edges.push(edge); - target.edges.push(edge); - } + function buildGraphEdge(source: FlowGraphNode, antecedent: FlowNode, seen: Set) { + const target = buildGraphNode(antecedent, seen); + const edge: FlowGraphEdge = { source, target }; + edges.push(edge); + source.edges.push(edge); + target.edges.push(edge); + } - function computeLevel(node: FlowGraphNode): number { - if (node.level !== -1) { - return node.level; - } - let level = 0; - for (const parent of getParents(node)) { - level = Math.max(level, computeLevel(parent) + 1); - } - return node.level = level; + function computeLevel(node: FlowGraphNode): number { + if (node.level !== -1) { + return node.level; } + let level = 0; + for (const parent of getParents(node)) { + level = Math.max(level, computeLevel(parent) + 1); + } + return node.level = level; + } - function computeHeight(node: FlowGraphNode): number { - let height = 0; - for (const child of getChildren(node)) { - height = Math.max(height, computeHeight(child)); - } - return height + 1; + function computeHeight(node: FlowGraphNode): number { + let height = 0; + for (const child of getChildren(node)) { + height = Math.max(height, computeHeight(child)); } + return height + 1; + } - function computeColumnWidths(height: number) { - const columns: number[] = fill(Array(height), 0); - for (const node of nodes) { - columns[node.level] = Math.max(columns[node.level], node.text.length); - } - return columns; + function computeColumnWidths(height: number) { + const columns: number[] = fill(Array(height), 0); + for (const node of nodes) { + columns[node.level] = Math.max(columns[node.level], node.text.length); } + return columns; + } - function computeLanes(node: FlowGraphNode, lane: number) { - if (node.lane === -1) { - node.lane = lane; - node.endLane = lane; - const children = getChildren(node); - for (let i = 0; i < children.length; i++) { - if (i > 0) lane++; - const child = children[i]; - computeLanes(child, lane); - if (child.endLane > node.endLane) { - lane = child.endLane; - } + function computeLanes(node: FlowGraphNode, lane: number) { + if (node.lane === -1) { + node.lane = lane; + node.endLane = lane; + const children = getChildren(node); + for (let i = 0; i < children.length; i++) { + if (i > 0) lane++; + const child = children[i]; + computeLanes(child, lane); + if (child.endLane > node.endLane) { + lane = child.endLane; } - node.endLane = lane; } + node.endLane = lane; } + } - function getHeader(flags: FlowFlags) { - if (flags & FlowFlags.Start) return "Start"; - if (flags & FlowFlags.BranchLabel) return "Branch"; - if (flags & FlowFlags.LoopLabel) return "Loop"; - if (flags & FlowFlags.Assignment) return "Assignment"; - if (flags & FlowFlags.TrueCondition) return "True"; - if (flags & FlowFlags.FalseCondition) return "False"; - if (flags & FlowFlags.SwitchClause) return "SwitchClause"; - if (flags & FlowFlags.ArrayMutation) return "ArrayMutation"; - if (flags & FlowFlags.Call) return "Call"; - if (flags & FlowFlags.ReduceLabel) return "ReduceLabel"; - if (flags & FlowFlags.Unreachable) return "Unreachable"; - throw new Error(); - } + function getHeader(flags: FlowFlags) { + if (flags & FlowFlags.Start) return "Start"; + if (flags & FlowFlags.BranchLabel) return "Branch"; + if (flags & FlowFlags.LoopLabel) return "Loop"; + if (flags & FlowFlags.Assignment) return "Assignment"; + if (flags & FlowFlags.TrueCondition) return "True"; + if (flags & FlowFlags.FalseCondition) return "False"; + if (flags & FlowFlags.SwitchClause) return "SwitchClause"; + if (flags & FlowFlags.ArrayMutation) return "ArrayMutation"; + if (flags & FlowFlags.Call) return "Call"; + if (flags & FlowFlags.ReduceLabel) return "ReduceLabel"; + if (flags & FlowFlags.Unreachable) return "Unreachable"; + throw new Error(); + } - function getNodeText(node: Node) { - const sourceFile = getSourceFileOfNode(node); - return getSourceTextOfNodeFromSourceFile(sourceFile, node, /*includeTrivia*/ false); - } + function getNodeText(node: Node) { + const sourceFile = getSourceFileOfNode(node); + return getSourceTextOfNodeFromSourceFile(sourceFile, node, /*includeTrivia*/ false); + } - function renderFlowNode(flowNode: FlowNode, circular: boolean | "circularity") { - let text = getHeader(flowNode.flags); - if (circular) { - text = `${text}#${getDebugFlowNodeId(flowNode)}`; + function renderFlowNode(flowNode: FlowNode, circular: boolean | "circularity") { + let text = getHeader(flowNode.flags); + if (circular) { + text = `${text}#${getDebugFlowNodeId(flowNode)}`; + } + if (hasNode(flowNode)) { + if (flowNode.node) { + text += ` (${getNodeText(flowNode.node)})`; } - if (hasNode(flowNode)) { - if (flowNode.node) { - text += ` (${getNodeText(flowNode.node)})`; + } + else if (isFlowSwitchClause(flowNode)) { + const clauses: string[] = []; + for (let i = flowNode.clauseStart; i < flowNode.clauseEnd; i++) { + const clause = flowNode.switchStatement.caseBlock.clauses[i]; + if (isDefaultClause(clause)) { + clauses.push("default"); } - } - else if (isFlowSwitchClause(flowNode)) { - const clauses: string[] = []; - for (let i = flowNode.clauseStart; i < flowNode.clauseEnd; i++) { - const clause = flowNode.switchStatement.caseBlock.clauses[i]; - if (isDefaultClause(clause)) { - clauses.push("default"); - } - else { - clauses.push(getNodeText(clause.expression)); - } + else { + clauses.push(getNodeText(clause.expression)); } - text += ` (${clauses.join(", ")})`; } - return circular === "circularity" ? `Circular(${text})` : text; + text += ` (${clauses.join(", ")})`; } + return circular === "circularity" ? `Circular(${text})` : text; + } - function renderGraph() { - const columnCount = columnWidths.length; - const laneCount = nodes.reduce((x, n) => Math.max(x, n.lane), 0) + 1; - const lanes: string[] = fill(Array(laneCount), ""); - const grid: (FlowGraphNode | undefined)[][] = columnWidths.map(() => Array(laneCount)); - const connectors: Connection[][] = columnWidths.map(() => fill(Array(laneCount), 0)); - - // build connectors - for (const node of nodes) { - grid[node.level][node.lane] = node; - const children = getChildren(node); - for (let i = 0; i < children.length; i++) { - const child = children[i]; - let connector: Connection = Connection.Right; - if (child.lane === node.lane) connector |= Connection.Left; - if (i > 0) connector |= Connection.Up; - if (i < children.length - 1) connector |= Connection.Down; - connectors[node.level][child.lane] |= connector; - } - if (children.length === 0) { - connectors[node.level][node.lane] |= Connection.NoChildren; - } - const parents = getParents(node); - for (let i = 0; i < parents.length; i++) { - const parent = parents[i]; - let connector: Connection = Connection.Left; - if (i > 0) connector |= Connection.Up; - if (i < parents.length - 1) connector |= Connection.Down; - connectors[node.level - 1][parent.lane] |= connector; - } + function renderGraph() { + const columnCount = columnWidths.length; + const laneCount = nodes.reduce((x, n) => Math.max(x, n.lane), 0) + 1; + const lanes: string[] = fill(Array(laneCount), ""); + const grid: (FlowGraphNode | undefined)[][] = columnWidths.map(() => Array(laneCount)); + const connectors: Connection[][] = columnWidths.map(() => fill(Array(laneCount), 0)); + + // build connectors + for (const node of nodes) { + grid[node.level][node.lane] = node; + const children = getChildren(node); + for (let i = 0; i < children.length; i++) { + const child = children[i]; + let connector: Connection = Connection.Right; + if (child.lane === node.lane) connector |= Connection.Left; + if (i > 0) connector |= Connection.Up; + if (i < children.length - 1) connector |= Connection.Down; + connectors[node.level][child.lane] |= connector; } + if (children.length === 0) { + connectors[node.level][node.lane] |= Connection.NoChildren; + } + const parents = getParents(node); + for (let i = 0; i < parents.length; i++) { + const parent = parents[i]; + let connector: Connection = Connection.Left; + if (i > 0) connector |= Connection.Up; + if (i < parents.length - 1) connector |= Connection.Down; + connectors[node.level - 1][parent.lane] |= connector; + } + } - // fill in missing connectors - for (let column = 0; column < columnCount; column++) { - for (let lane = 0; lane < laneCount; lane++) { - const left = column > 0 ? connectors[column - 1][lane] : 0; - const above = lane > 0 ? connectors[column][lane - 1] : 0; - let connector = connectors[column][lane]; - if (!connector) { - if (left & Connection.Right) connector |= Connection.LeftRight; - if (above & Connection.Down) connector |= Connection.UpDown; - connectors[column][lane] = connector; - } + // fill in missing connectors + for (let column = 0; column < columnCount; column++) { + for (let lane = 0; lane < laneCount; lane++) { + const left = column > 0 ? connectors[column - 1][lane] : 0; + const above = lane > 0 ? connectors[column][lane - 1] : 0; + let connector = connectors[column][lane]; + if (!connector) { + if (left & Connection.Right) connector |= Connection.LeftRight; + if (above & Connection.Down) connector |= Connection.UpDown; + connectors[column][lane] = connector; } } + } - for (let column = 0; column < columnCount; column++) { - for (let lane = 0; lane < lanes.length; lane++) { - const connector = connectors[column][lane]; - const fill = connector & Connection.Left ? BoxCharacter.lr : " "; - const node = grid[column][lane]; - if (!node) { - if (column < columnCount - 1) { - writeLane(lane, repeat(fill, columnWidths[column] + 1)); - } + for (let column = 0; column < columnCount; column++) { + for (let lane = 0; lane < lanes.length; lane++) { + const connector = connectors[column][lane]; + const fill = connector & Connection.Left ? BoxCharacter.lr : " "; + const node = grid[column][lane]; + if (!node) { + if (column < columnCount - 1) { + writeLane(lane, repeat(fill, columnWidths[column] + 1)); } - else { - writeLane(lane, node.text); - if (column < columnCount - 1) { - writeLane(lane, " "); - writeLane(lane, repeat(fill, columnWidths[column] - node.text.length)); - } + } + else { + writeLane(lane, node.text); + if (column < columnCount - 1) { + writeLane(lane, " "); + writeLane(lane, repeat(fill, columnWidths[column] - node.text.length)); } - writeLane(lane, getBoxCharacter(connector)); - writeLane(lane, connector & Connection.Right && column < columnCount - 1 && !grid[column + 1][lane] ? BoxCharacter.lr : " "); } + writeLane(lane, getBoxCharacter(connector)); + writeLane(lane, connector & Connection.Right && column < columnCount - 1 && !grid[column + 1][lane] ? BoxCharacter.lr : " "); } + } - return `\n${lanes.join("\n")}\n`; + return `\n${lanes.join("\n")}\n`; - function writeLane(lane: number, text: string) { - lanes[lane] += text; - } + function writeLane(lane: number, text: string) { + lanes[lane] += text; } + } - function getBoxCharacter(connector: Connection) { - switch (connector) { - case Connection.UpDown: return BoxCharacter.ud; - case Connection.LeftRight: return BoxCharacter.lr; - case Connection.UpLeft: return BoxCharacter.ul; - case Connection.UpRight: return BoxCharacter.ur; - case Connection.DownLeft: return BoxCharacter.dl; - case Connection.DownRight: return BoxCharacter.dr; - case Connection.UpDownLeft: return BoxCharacter.udl; - case Connection.UpDownRight: return BoxCharacter.udr; - case Connection.UpLeftRight: return BoxCharacter.ulr; - case Connection.DownLeftRight: return BoxCharacter.dlr; - case Connection.UpDownLeftRight: return BoxCharacter.udlr; - } - return " "; + function getBoxCharacter(connector: Connection) { + switch (connector) { + case Connection.UpDown: return BoxCharacter.ud; + case Connection.LeftRight: return BoxCharacter.lr; + case Connection.UpLeft: return BoxCharacter.ul; + case Connection.UpRight: return BoxCharacter.ur; + case Connection.DownLeft: return BoxCharacter.dl; + case Connection.DownRight: return BoxCharacter.dr; + case Connection.UpDownLeft: return BoxCharacter.udl; + case Connection.UpDownRight: return BoxCharacter.udr; + case Connection.UpLeftRight: return BoxCharacter.ulr; + case Connection.DownLeftRight: return BoxCharacter.dlr; + case Connection.UpDownLeftRight: return BoxCharacter.udlr; } + return " "; + } - function fill(array: T[], value: T) { - if (array.fill) { - array.fill(value); - } - else { - for (let i = 0; i < array.length; i++) { - array[i] = value; - } + function fill(array: T[], value: T) { + if (array.fill) { + array.fill(value); + } + else { + for (let i = 0; i < array.length; i++) { + array[i] = value; } - return array; } + return array; + } - function repeat(ch: string, length: number) { - if (ch.repeat) { - return length > 0 ? ch.repeat(length) : ""; - } - let s = ""; - while (s.length < length) { - s += ch; - } - return s; + function repeat(ch: string, length: number) { + if (ch.repeat) { + return length > 0 ? ch.repeat(length) : ""; + } + let s = ""; + while (s.length < length) { + s += ch; } + return s; } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index da4f3f56fa5e7..a8699c3dc6456 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1,16 +1,162 @@ -import * as ts from "./_namespaces/ts"; +/* eslint-disable */ +import * as types from "./types"; +/* eslint-enable */ + +import * as Debug from "./debug"; +import * as performance from "./performance"; +import { + computeSignature, + ProgramBuildInfo, + ProgramBundleEmitBuildInfo, +} from "./builder"; +import { OutputFile } from "./builderStatePublic"; +import { + arrayToMap, + cast, + clone, + contains, + createGetCanonicalFileName, + createMultiMap, + emptyArray, + every, + filter, + findIndex, + firstOrUndefined, + forEach, + GetCanonicalFileName, + isArray, + last, + lastOrUndefined, + length, + maybeBind, + memoize, + notImplemented, + returnFalse, + returnUndefined, + singleOrUndefined, + some, + stableSort, + stringContains, + tryCast, +} from "./core"; +import { Comparison, version } from "./corePublic"; +import { + createBinaryExpressionTrampoline, +} from "./factory/binaryExpressionStateMachine"; +import { compareEmitHelpers } from "./factory/emitHelpers"; +import { + getCommentRange, + getConstantValue, + getEmitHelpers, + getIdentifierTypeArguments, + getSnippetElement, + getSourceMapRange, + getStartsOnNewLine, + getSyntheticLeadingComments, + getSyntheticTrailingComments, + getTypeNode, +} from "./factory/emitNode"; +import { + createInputFilesWithFileTexts, + factory, +} from "./factory/nodeFactory"; +import { + isArrowFunction, + isBinaryExpression, + isBlock, + isBundle, + isDecorator, + isEmptyStatement, + isExportAssignment, + isExportSpecifier, + isIdentifier, + isJsxClosingElement, + isJsxOpeningElement, + isModuleDeclaration, + isNumericLiteral, + isParenthesizedExpression, + isPartiallyEmittedExpression, + isPrivateIdentifier, + isSourceFile, + isStringLiteral, + isTypeParameterDeclaration, + isUnparsedPrepend, + isUnparsedSource, + isVariableStatement, +} from "./factory/nodeTests"; +import { + formatGeneratedName, + formatGeneratedNamePart, + getExternalHelpersModuleName, + getNodeForGeneratedName, + hasRecordedExternalHelpers, +} from "./factory/utilities"; +import { + setOriginalNode, + setTextRange, +} from "./factory/utilitiesPublic"; +import { + forEachChild, + isDeclarationFileName, + isJSDocLikeText, +} from "./parser"; +import { + combinePaths, + comparePaths, + directorySeparator, + ensurePathIsNonModuleName, + ensureTrailingDirectorySeparator, + fileExtensionIs, + fileExtensionIsOneOf, + getBaseFileName, + getDirectoryPath, + getNormalizedAbsolutePath, + getRelativePathFromDirectory, + getRelativePathToDirectoryOrUrl, + getRootLength, + normalizePath, + normalizeSlashes, + resolvePath, +} from "./path"; +import { + computeCommonSourceDirectoryOfFilenames, + createPrependNodes, +} from "./program"; +import { + computeLineStarts, + forEachLeadingCommentRange, + forEachTrailingCommentRange, + getLeadingCommentRanges, + getLineAndCharacterOfPosition, + getLineStarts, + getShebang, + getTrailingCommentRanges, + skipTrivia, + tokenToString, +} from "./scanner"; +import { + createSourceMapGenerator, + tryParseRawSourceMap, +} from "./sourcemap"; +import { sys } from "./sys"; +import { tracing } from "./tracing"; +import { + getTransformers, + noEmitNotification, + noEmitSubstitution, + transformNodes, +} from "./transformer"; +import { isInternalDeclaration } from "./transformers/declarations"; import { AccessorDeclaration, ArrayBindingPattern, ArrayLiteralExpression, - arrayToMap, ArrayTypeNode, ArrowFunction, AsExpression, AssertClause, AssertEntry, AwaitExpression, - base64encode, BigIntLiteral, BinaryExpression, BinaryOperatorToken, @@ -28,78 +174,45 @@ import { BundleFileTextLikeKind, CallExpression, CallSignatureDeclaration, - canHaveLocals, CaseBlock, CaseClause, CaseOrDefaultClause, - cast, CatchClause, - changeExtension, CharacterCodes, ClassDeclaration, ClassExpression, ClassStaticBlockDeclaration, - clone, - combinePaths, CommaListExpression, CommentRange, - compareEmitHelpers, - comparePaths, - Comparison, CompilerHost, CompilerOptions, - computeCommonSourceDirectoryOfFilenames, ComputedPropertyName, - computeLineStarts, - computeSignature, ConditionalExpression, ConditionalTypeNode, ConstructorDeclaration, ConstructorTypeNode, ConstructSignatureDeclaration, - contains, ContinueStatement, - createBinaryExpressionTrampoline, - createDiagnosticCollection, - createGetCanonicalFileName, - createInputFilesWithFileTexts, - createMultiMap, - createPrependNodes, - createSourceMapGenerator, - createTextWriter, CustomTransformers, - Debug, DebuggerStatement, DeclarationName, Decorator, DefaultClause, DeleteExpression, - directorySeparator, DoStatement, DotToken, ElementAccessExpression, - emitDetachedComments, - EmitFileNames, EmitFlags, EmitHint, EmitHost, - emitNewLineBeforeLeadingCommentOfPosition, EmitOnly, EmitResolver, EmitResult, EmitTextWriter, EmitTransformers, - emptyArray, - ensurePathIsNonModuleName, - ensureTrailingDirectorySeparator, EntityName, EnumDeclaration, EnumMember, - escapeJsxAttributeString, - escapeLeadingUnderscores, - escapeNonAsciiString, - escapeString, - every, ExportAssignment, ExportDeclaration, ExportSpecifier, @@ -108,21 +221,9 @@ import { ExpressionWithTypeArguments, Extension, ExternalModuleReference, - factory, - fileExtensionIs, - fileExtensionIsOneOf, FileReference, - filter, - findIndex, - firstOrUndefined, - forEach, - forEachChild, - forEachLeadingCommentRange, - forEachTrailingCommentRange, ForInOrOfStatement, ForInStatement, - formatGeneratedName, - formatGeneratedNamePart, ForOfStatement, ForStatement, FunctionDeclaration, @@ -133,62 +234,9 @@ import { GeneratedIdentifierFlags, GeneratedNamePart, GeneratedPrivateIdentifier, - getAreDeclarationMapsEnabled, - getBaseFileName, - GetCanonicalFileName, - getCommentRange, - getConstantValue, - getContainingNodeArray, - getDeclarationEmitExtensionForPath, - getDeclarationEmitOutputFilePath, - getDirectoryPath, - getEmitDeclarations, - getEmitFlags, - getEmitHelpers, - getEmitModuleKind, - getExternalHelpersModuleName, - getExternalModuleName, - getIdentifierTypeArguments, - getInternalEmitFlags, - getLeadingCommentRanges, - getLineAndCharacterOfPosition, - getLinesBetweenPositionAndNextNonWhitespaceCharacter, - getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter, - getLinesBetweenRangeEndAndRangeStart, - getLineStarts, - getLiteralText, - GetLiteralTextFlags, - getNewLineCharacter, - getNodeForGeneratedName, - getNodeId, - getNormalizedAbsolutePath, - getOriginalNode, - getOwnEmitOutputFilePath, - getParseTreeNode, - getRelativePathFromDirectory, - getRelativePathToDirectoryOrUrl, - getRootLength, - getShebang, - getSnippetElement, - getSourceFileOfNode, - getSourceFilePathInNewDir, - getSourceFilesToEmit, - getSourceMapRange, - getSourceTextOfNodeFromSourceFile, - getStartsOnNewLine, - getSyntheticLeadingComments, - getSyntheticTrailingComments, - getTextOfJSDocComment, - getTrailingCommentRanges, - getTrailingSemicolonDeferringWriter, - getTransformers, - getTypeNode, - guessIndentation, HasLocals, - hasRecordedExternalHelpers, HeritageClause, Identifier, - idText, IfStatement, ImportClause, ImportDeclaration, @@ -202,58 +250,6 @@ import { InterfaceDeclaration, InternalEmitFlags, IntersectionTypeNode, - isAccessExpression, - isArray, - isArrowFunction, - isBinaryExpression, - isBindingPattern, - isBlock, - isBundle, - isBundleFileTextLike, - isDeclaration, - isDeclarationFileName, - isDecorator, - isEmptyStatement, - isExportAssignment, - isExportSpecifier, - isExpression, - isFileLevelUniqueName, - isFunctionLike, - isGeneratedIdentifier, - isGeneratedPrivateIdentifier, - isIdentifier, - isIncrementalCompilation, - isInJsonFile, - isInternalDeclaration, - isJSDocLikeText, - isJsonSourceFile, - isJsxClosingElement, - isJsxOpeningElement, - isKeyword, - isLet, - isLiteralExpression, - isMemberName, - isModifier, - isModuleDeclaration, - isNodeDescendantOf, - isNumericLiteral, - isParenthesizedExpression, - isPartiallyEmittedExpression, - isPinnedComment, - isPrivateIdentifier, - isPrologueDirective, - isRecognizedTripleSlashComment, - isSourceFile, - isSourceFileNotJson, - isStringLiteral, - isTemplateLiteralKind, - isTokenKind, - isTypeParameterDeclaration, - isUnparsedNode, - isUnparsedPrepend, - isUnparsedSource, - isVarConst, - isVariableStatement, JSDoc, JSDocAugmentsTag, JSDocCallbackTag, @@ -297,18 +293,12 @@ import { JsxTagNameExpression, JsxText, LabeledStatement, - last, - lastOrUndefined, LateBoundDeclaration, - length, ListFormat, LiteralExpression, LiteralLikeNode, LiteralTypeNode, - makeIdentifierFromModuleName, MappedTypeNode, - maybeBind, - memoize, MetaProperty, MethodDeclaration, MethodSignature, @@ -318,7 +308,6 @@ import { ModuleDeclaration, ModuleKind, ModuleReference, - moveRangePastModifiers, NamedDeclaration, NamedExports, NamedImports, @@ -331,68 +320,37 @@ import { Node, NodeArray, NodeFlags, - nodeIsSynthesized, - noEmitNotification, - noEmitSubstitution, NonNullExpression, - normalizePath, - normalizeSlashes, - notImplemented, NumericLiteral, ObjectBindingPattern, ObjectLiteralExpression, OptionalTypeNode, - outFile, - OutputFile, ParameterDeclaration, ParenthesizedExpression, ParenthesizedTypeNode, ParsedCommandLine, PartiallyEmittedExpression, Placeholder, - positionIsSynthesized, - positionsAreOnSameLine, PostfixUnaryExpression, PrefixUnaryExpression, Printer, PrinterOptions, PrintHandlers, PrivateIdentifier, - ProgramBuildInfo, - ProgramBundleEmitBuildInfo, ProjectReference, PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, PropertySignature, QualifiedName, - rangeEndIsOnSameLineAsRangeStart, - rangeEndPositionsAreOnSameLine, - rangeIsOnSingleLine, - rangeStartPositionsAreOnSameLine, - readJsonOrUndefined, - removeFileExtension, - resolvePath, RestTypeNode, - returnFalse, ReturnStatement, - returnUndefined, SatisfiesExpression, ScriptTarget, - setEachParent, - setOriginalNode, - setParent, - setTextRange, - setTextRangePosEnd, - setTextRangePosWidth, ShorthandPropertyAssignment, SignatureDeclaration, - singleOrUndefined, - skipPartiallyEmittedExpressions, - skipTrivia, SnippetElement, SnippetKind, - some, SourceFile, SourceFilePrologueDirective, SourceFilePrologueInfo, @@ -401,17 +359,13 @@ import { SourceMapSource, SpreadAssignment, SpreadElement, - stableSort, Statement, - stringContains, StringLiteral, - supportedJSExtensionsFlat, SwitchStatement, Symbol, SymbolFlags, SyntaxKind, SynthesizedComment, - sys, TabStop, TaggedTemplateExpression, TemplateExpression, @@ -421,12 +375,7 @@ import { TextRange, ThrowStatement, TokenFlags, - tokenToString, - tracing, TransformationResult, - transformNodes, - tryCast, - tryParseRawSourceMap, TryStatement, TupleTypeNode, TypeAliasDeclaration, @@ -449,16 +398,107 @@ import { VariableDeclaration, VariableDeclarationList, VariableStatement, - version, VoidExpression, WhileStatement, WithStatement, - writeCommentRange, - writeFile, WriteFileCallbackData, YieldExpression, -} from "./_namespaces/ts"; -import * as performance from "./_namespaces/ts.performance"; +} from "./types"; +import { + base64encode, + createDiagnosticCollection, + createTextWriter, + emitDetachedComments, + EmitFileNames, + emitNewLineBeforeLeadingCommentOfPosition, + escapeJsxAttributeString, + escapeNonAsciiString, + escapeString, + getAreDeclarationMapsEnabled, + getContainingNodeArray, + getEmitDeclarations, + getEmitFlags, + getEmitModuleKind, + getExternalModuleName, + getInternalEmitFlags, + getLinesBetweenPositionAndNextNonWhitespaceCharacter, + getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter, + getLinesBetweenRangeEndAndRangeStart, + getLiteralText, + GetLiteralTextFlags, + getNewLineCharacter, + getNodeId, + getSourceFileOfNode, + getSourceTextOfNodeFromSourceFile, + getTrailingSemicolonDeferringWriter, + isAccessExpression, + isBundleFileTextLike, + isFileLevelUniqueName, + isIncrementalCompilation, + isInJsonFile, + isJsonSourceFile, + isKeyword, + isLet, + isNodeDescendantOf, + isPinnedComment, + isPrologueDirective, + isRecognizedTripleSlashComment, + isSourceFileNotJson, + isVarConst, + makeIdentifierFromModuleName, + moveRangePastModifiers, + nodeIsSynthesized, + outFile, + positionsAreOnSameLine, + rangeEndIsOnSameLineAsRangeStart, + rangeEndPositionsAreOnSameLine, + rangeIsOnSingleLine, + rangeStartPositionsAreOnSameLine, + setTextRangePosEnd, + setTextRangePosWidth, + writeCommentRange, + writeFile, +} from "./utilities"; +import { + canHaveLocals, + escapeLeadingUnderscores, + getOriginalNode, + getParseTreeNode, + getTextOfJSDocComment, + guessIndentation, + idText, + isBindingPattern, + isDeclaration, + isExpression, + isFunctionLike, + isGeneratedIdentifier, + isGeneratedPrivateIdentifier, + isLiteralExpression, + isMemberName, + isModifier, + isTemplateLiteralKind, + isTokenKind, + isUnparsedNode, + skipPartiallyEmittedExpressions, +} from "./utilitiesPublic"; +import { positionIsSynthesized } from "./scannerUtilities"; +import { + getDeclarationEmitOutputFilePath, + getOwnEmitOutputFilePath, + getSourceFilePathInNewDir, + getSourceFilesToEmit, +} from "./emitterUtilities"; +import { + setEachParent, + setParent, +} from "./parserUtilities"; +import { readJsonOrUndefined } from "./commandLineParserUtilities"; +import { + changeExtension, + getDeclarationEmitExtensionForPath, + removeFileExtension, + supportedJSExtensionsFlat, +} from "./extension"; const brackets = createBracketsMap(); @@ -1123,7 +1163,7 @@ export function getBuildInfoText(buildInfo: BuildInfo) { } /** @internal */ -export function getBuildInfo(buildInfoFile: string, buildInfoText: string) { +export function getBuildInfo(buildInfoFile: string, buildInfoText: string): BuildInfo | undefined { return readJsonOrUndefined(buildInfoFile, buildInfoText) as BuildInfo | undefined; } @@ -1352,16 +1392,16 @@ const enum PipelinePhase { } /** @internal */ -export const createPrinterWithDefaults = /* @__PURE__ */ memoize(() => createPrinter({})); +export const createPrinterWithDefaults: () => Printer = /* @__PURE__ */ memoize(() => createPrinter({})); /** @internal */ -export const createPrinterWithRemoveComments = /* @__PURE__ */ memoize(() => createPrinter({ removeComments: true })); +export const createPrinterWithRemoveComments: () => Printer = /* @__PURE__ */ memoize(() => createPrinter({ removeComments: true })); /** @internal */ -export const createPrinterWithRemoveCommentsNeverAsciiEscape = /* @__PURE__ */ memoize(() => createPrinter({ removeComments: true, neverAsciiEscape: true })); +export const createPrinterWithRemoveCommentsNeverAsciiEscape: () => Printer = /* @__PURE__ */ memoize(() => createPrinter({ removeComments: true, neverAsciiEscape: true })); /** @internal */ -export const createPrinterWithRemoveCommentsOmitTrailingSemicolon = /* @__PURE__ */ memoize(() => createPrinter({ removeComments: true, omitTrailingSemicolon: true })); +export const createPrinterWithRemoveCommentsOmitTrailingSemicolon: () => Printer = /* @__PURE__ */ memoize(() => createPrinter({ removeComments: true, omitTrailingSemicolon: true })); export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer { // Why var? It avoids TDZ checks in the runtime which can be costly. @@ -6056,7 +6096,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri ); } - return Debug.fail(`Unsupported GeneratedIdentifierKind: ${Debug.formatEnum(autoGenerate.flags & GeneratedIdentifierFlags.KindMask, (ts as any).GeneratedIdentifierFlags, /*isFlags*/ true)}.`); + return Debug.fail(`Unsupported GeneratedIdentifierKind: ${Debug.formatEnum(autoGenerate.flags & GeneratedIdentifierFlags.KindMask, (types as any).GeneratedIdentifierFlags, /*isFlags*/ true)}.`); } // Comments diff --git a/src/compiler/emitterUtilities.ts b/src/compiler/emitterUtilities.ts new file mode 100644 index 0000000000000..3e37c6cf0a906 --- /dev/null +++ b/src/compiler/emitterUtilities.ts @@ -0,0 +1,100 @@ +import { + filter, + GetCanonicalFileName, +} from "./core"; +import { + getDeclarationEmitExtensionForPath, + removeFileExtension, +} from "./extension"; +import { + combinePaths, + getNormalizedAbsolutePath, +} from "./path"; +import { + CompilerOptions, + EmitHost, + ModuleKind, + SourceFile, +} from "./types"; +import { + getEmitModuleKind, + isExternalModule, + outFile, + sourceFileMayBeEmitted, +} from "./utilities"; + +/** + * Gets the source files that are expected to have an emit output. + * + * Originally part of `forEachExpectedEmitFile`, this functionality was extracted to support + * transformations. + * + * @param host An EmitHost. + * @param targetSourceFile An optional target source file to emit. + * + * @internal + */ +export function getSourceFilesToEmit(host: EmitHost, targetSourceFile?: SourceFile, forceDtsEmit?: boolean): readonly SourceFile[] { + const options = host.getCompilerOptions(); + if (outFile(options)) { + const moduleKind = getEmitModuleKind(options); + const moduleEmitEnabled = options.emitDeclarationOnly || moduleKind === ModuleKind.AMD || moduleKind === ModuleKind.System; + // Can emit only sources that are not declaration file and are either non module code or module with --module or --target es6 specified + return filter( + host.getSourceFiles(), + sourceFile => + (moduleEmitEnabled || !isExternalModule(sourceFile)) && + sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit) + ); + } + else { + const sourceFiles = targetSourceFile === undefined ? host.getSourceFiles() : [targetSourceFile]; + return filter( + sourceFiles, + sourceFile => sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit) + ); + } +} + +/** @internal */ +export function getDeclarationEmitOutputFilePath(fileName: string, host: EmitHost) { + return getDeclarationEmitOutputFilePathWorker(fileName, host.getCompilerOptions(), host.getCurrentDirectory(), host.getCommonSourceDirectory(), f => host.getCanonicalFileName(f)); +} + +/** @internal */ +export function getDeclarationEmitOutputFilePathWorker(fileName: string, options: CompilerOptions, currentDirectory: string, commonSourceDirectory: string, getCanonicalFileName: GetCanonicalFileName): string { + const outputDir = options.declarationDir || options.outDir; // Prefer declaration folder if specified + + const path = outputDir + ? getSourceFilePathInNewDirWorker(fileName, outputDir, currentDirectory, commonSourceDirectory, getCanonicalFileName) + : fileName; + const declarationExtension = getDeclarationEmitExtensionForPath(path); + return removeFileExtension(path) + declarationExtension; +} + +/** @internal */ +export function getSourceFilePathInNewDir(fileName: string, host: EmitHost, newDirPath: string): string { + return getSourceFilePathInNewDirWorker(fileName, newDirPath, host.getCurrentDirectory(), host.getCommonSourceDirectory(), f => host.getCanonicalFileName(f)); +} + +/** @internal */ +export function getSourceFilePathInNewDirWorker(fileName: string, newDirPath: string, currentDirectory: string, commonSourceDirectory: string, getCanonicalFileName: GetCanonicalFileName): string { + let sourceFilePath = getNormalizedAbsolutePath(fileName, currentDirectory); + const isSourceFileInCommonSourceDirectory = getCanonicalFileName(sourceFilePath).indexOf(getCanonicalFileName(commonSourceDirectory)) === 0; + sourceFilePath = isSourceFileInCommonSourceDirectory ? sourceFilePath.substring(commonSourceDirectory.length) : sourceFilePath; + return combinePaths(newDirPath, sourceFilePath); +} + +/** @internal */ +export function getOwnEmitOutputFilePath(fileName: string, host: EmitHost, extension: string) { + const compilerOptions = host.getCompilerOptions(); + let emitOutputFilePathWithoutExtension: string; + if (compilerOptions.outDir) { + emitOutputFilePathWithoutExtension = removeFileExtension(getSourceFilePathInNewDir(fileName, host, compilerOptions.outDir)); + } + else { + emitOutputFilePathWithoutExtension = removeFileExtension(fileName); + } + + return emitOutputFilePathWithoutExtension + extension; +} diff --git a/src/compiler/extension.ts b/src/compiler/extension.ts new file mode 100644 index 0000000000000..7416616cb5706 --- /dev/null +++ b/src/compiler/extension.ts @@ -0,0 +1,202 @@ +import { + endsWith, + find, + flatten, + mapDefined, + some, + startsWith, +} from "./core"; +import * as Debug from "./debug"; +import { + changeAnyExtension, + fileExtensionIs, + fileExtensionIsOneOf, +} from "./path"; +import { + CompilerOptions, + Extension, + FileExtensionInfo, + Path, + ScriptKind, +} from "./types"; +import { + getAllowJSCompilerOption, + getResolveJsonModule, + isJSLike, +} from "./utilities"; + +/** + * Groups of supported extensions in order of file resolution precedence. (eg, TS > TSX > DTS and seperately, CTS > DCTS) + * + * @internal + */ +export const supportedTSExtensions: readonly Extension[][] = [[Extension.Ts, Extension.Tsx, Extension.Dts], [Extension.Cts, Extension.Dcts], [Extension.Mts, Extension.Dmts]]; + +/** @internal */ +export const supportedTSExtensionsFlat: readonly Extension[] = flatten(supportedTSExtensions); + +/** @internal */ +export const supportedTSExtensionsWithJson: readonly Extension[][] = [...supportedTSExtensions, [Extension.Json]]; + +/** + * Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". + * @internal + */ +export const supportedTSExtensionsForExtractExtension: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts, Extension.Cts, Extension.Mts, Extension.Ts, Extension.Tsx]; + +/** @internal */ +export const supportedJSExtensions: readonly Extension[][] = [[Extension.Js, Extension.Jsx], [Extension.Mjs], [Extension.Cjs]]; + +/** @internal */ +export const supportedJSExtensionsFlat: readonly Extension[] = flatten(supportedJSExtensions); + +/** @internal */ +export const allSupportedExtensions: readonly Extension[][] = [[Extension.Ts, Extension.Tsx, Extension.Dts, Extension.Js, Extension.Jsx], [Extension.Cts, Extension.Dcts, Extension.Cjs], [Extension.Mts, Extension.Dmts, Extension.Mjs]]; + +/** @internal */ +export const allSupportedExtensionsWithJson: readonly Extension[][] = [...allSupportedExtensions, [Extension.Json]]; + +/** @internal */ +export const supportedDeclarationExtensions: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts]; + +/** @internal */ +export const supportedTSImplementationExtensions: readonly Extension[] = [Extension.Ts, Extension.Cts, Extension.Mts, Extension.Tsx]; + +/** @internal */ +export const extensionsNotSupportingExtensionlessResolution: readonly Extension[] = [Extension.Mts, Extension.Dmts, Extension.Mjs, Extension.Cts, Extension.Dcts, Extension.Cjs]; + +const extensionsToRemove = [Extension.Dts, Extension.Dmts, Extension.Dcts, Extension.Mjs, Extension.Mts, Extension.Cjs, Extension.Cts, Extension.Ts, Extension.Js, Extension.Tsx, Extension.Jsx, Extension.Json]; + +/** + * Return ".ts", ".d.ts", or ".tsx", if that is the extension. + * + * @internal + */ +export function tryExtractTSExtension(fileName: string): string | undefined { + return find(supportedTSExtensionsForExtractExtension, extension => fileExtensionIs(fileName, extension)); +} + +/** @internal */ +export function getSupportedExtensions(options?: CompilerOptions): readonly Extension[][]; +/** @internal */ +export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]): readonly string[][]; +/** @internal */ +export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]): readonly string[][] { + const needJsExtensions = options && getAllowJSCompilerOption(options); + + if (!extraFileExtensions || extraFileExtensions.length === 0) { + return needJsExtensions ? allSupportedExtensions : supportedTSExtensions; + } + + const builtins = needJsExtensions ? allSupportedExtensions : supportedTSExtensions; + const flatBuiltins = flatten(builtins); + const extensions = [ + ...builtins, + ...mapDefined(extraFileExtensions, x => x.scriptKind === ScriptKind.Deferred || needJsExtensions && isJSLike(x.scriptKind) && flatBuiltins.indexOf(x.extension as Extension) === -1 ? [x.extension] : undefined) + ]; + + return extensions; +} + +/** @internal */ +export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly Extension[][]): readonly Extension[][]; +/** @internal */ +export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly string[][]): readonly string[][]; +/** @internal */ +export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly string[][]): readonly string[][] { + if (!options || !getResolveJsonModule(options)) return supportedExtensions; + if (supportedExtensions === allSupportedExtensions) return allSupportedExtensionsWithJson; + if (supportedExtensions === supportedTSExtensions) return supportedTSExtensionsWithJson; + return [...supportedExtensions, [Extension.Json]]; +} + +/** @internal */ +export function hasJSFileExtension(fileName: string): boolean { + return some(supportedJSExtensionsFlat, extension => fileExtensionIs(fileName, extension)); +} + +/** @internal */ +export function hasTSFileExtension(fileName: string): boolean { + return some(supportedTSExtensionsFlat, extension => fileExtensionIs(fileName, extension)); +} + +/** @internal */ +export function getDeclarationEmitExtensionForPath(path: string) { + return fileExtensionIsOneOf(path, [Extension.Mjs, Extension.Mts]) ? Extension.Dmts : + fileExtensionIsOneOf(path, [Extension.Cjs, Extension.Cts]) ? Extension.Dcts : + fileExtensionIsOneOf(path, [Extension.Json]) ? `.d.json.ts` : // Drive-by redefinition of json declaration file output name so if it's ever enabled, it behaves well + Extension.Dts; +} + +/** + * This function is an inverse of `getDeclarationEmitExtensionForPath`. + * + * @internal + */ +export function getPossibleOriginalInputExtensionForExtension(path: string) { + return fileExtensionIsOneOf(path, [Extension.Dmts, Extension.Mjs, Extension.Mts]) ? [Extension.Mts, Extension.Mjs] : + fileExtensionIsOneOf(path, [Extension.Dcts, Extension.Cjs, Extension.Cts]) ? [Extension.Cts, Extension.Cjs]: + fileExtensionIsOneOf(path, [`.d.json.ts`]) ? [Extension.Json] : + [Extension.Tsx, Extension.Ts, Extension.Jsx, Extension.Js]; +} + +/** @internal */ +export function removeFileExtension(path: string): string { + for (const ext of extensionsToRemove) { + const extensionless = tryRemoveExtension(path, ext); + if (extensionless !== undefined) { + return extensionless; + } + } + return path; +} + +/** @internal */ +export function tryRemoveExtension(path: string, extension: string): string | undefined { + return fileExtensionIs(path, extension) ? removeExtension(path, extension) : undefined; +} + +/** @internal */ +export function removeExtension(path: string, extension: string): string { + return path.substring(0, path.length - extension.length); +} + +/** @internal */ +export function changeExtension(path: T, newExtension: string): T { + return changeAnyExtension(path, newExtension, extensionsToRemove, /*ignoreCase*/ false) as T; +} + +/** + * True if an extension is one of the supported TypeScript extensions. + * + * @internal + */ +export function extensionIsTS(ext: string): boolean { + return ext === Extension.Ts || ext === Extension.Tsx || ext === Extension.Dts || ext === Extension.Cts || ext === Extension.Mts || ext === Extension.Dmts || ext === Extension.Dcts || (startsWith(ext, ".d.") && endsWith(ext, ".ts")); +} + +/** @internal */ +export function resolutionExtensionIsTSOrJson(ext: string) { + return extensionIsTS(ext) || ext === Extension.Json; +} + +/** + * Gets the extension from a path. + * Path must have a valid extension. + * + * @internal + */ +export function extensionFromPath(path: string): Extension { + const ext = tryGetExtensionFromPath(path); + return ext !== undefined ? ext : Debug.fail(`File ${path} has unknown extension.`); +} + +/** @internal */ +export function isAnySupportedFileExtension(path: string): boolean { + return tryGetExtensionFromPath(path) !== undefined; +} + +/** @internal */ +export function tryGetExtensionFromPath(path: string): Extension | undefined { + return find(extensionsToRemove, e => fileExtensionIs(path, e)); +} diff --git a/src/compiler/factory/baseNodeFactory.ts b/src/compiler/factory/baseNodeFactory.ts index 5641e25703ebe..1391644315bfb 100644 --- a/src/compiler/factory/baseNodeFactory.ts +++ b/src/compiler/factory/baseNodeFactory.ts @@ -1,8 +1,8 @@ +import { objectAllocator } from "../objectAllocator"; import { Node, - objectAllocator, SyntaxKind, -} from "../_namespaces/ts"; +} from "../types"; /** * A `BaseNodeFactory` is an abstraction over an `ObjectAllocator` that handles caching `Node` constructors diff --git a/src/compiler/factory/binaryExpressionStateMachine.ts b/src/compiler/factory/binaryExpressionStateMachine.ts new file mode 100644 index 0000000000000..7b3614472a8b4 --- /dev/null +++ b/src/compiler/factory/binaryExpressionStateMachine.ts @@ -0,0 +1,219 @@ +import * as Debug from "../debug"; +import { + BinaryExpression, + BinaryOperatorToken, + Expression, +} from "../types"; + +type BinaryExpressionState = (machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }, outerState: TOuterState) => number; + +/** + * Handles walking into a `BinaryExpression`. + * @param machine State machine handler functions + * @param frame The current frame + * @returns The new frame + */ +function enter(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, outerState: TOuterState): number { + const prevUserState = stackIndex > 0 ? userStateStack[stackIndex - 1] : undefined; + Debug.assertEqual(stateStack[stackIndex], enter); + userStateStack[stackIndex] = machine.onEnter(nodeStack[stackIndex], prevUserState, outerState); + stateStack[stackIndex] = nextState(machine, enter); + return stackIndex; +} + +/** + * Handles walking the `left` side of a `BinaryExpression`. + * @param machine State machine handler functions + * @param frame The current frame + * @returns The new frame + */ +function left(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { + Debug.assertEqual(stateStack[stackIndex], left); + Debug.assertIsDefined(machine.onLeft); + stateStack[stackIndex] = nextState(machine, left); + const nextNode = machine.onLeft(nodeStack[stackIndex].left, userStateStack[stackIndex], nodeStack[stackIndex]); + if (nextNode) { + checkCircularity(stackIndex, nodeStack, nextNode); + return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode); + } + return stackIndex; +} + +/** + * Handles walking the `operatorToken` of a `BinaryExpression`. + * @param machine State machine handler functions + * @param frame The current frame + * @returns The new frame + */ +function operator(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { + Debug.assertEqual(stateStack[stackIndex], operator); + Debug.assertIsDefined(machine.onOperator); + stateStack[stackIndex] = nextState(machine, operator); + machine.onOperator(nodeStack[stackIndex].operatorToken, userStateStack[stackIndex], nodeStack[stackIndex]); + return stackIndex; +} + +/** + * Handles walking the `right` side of a `BinaryExpression`. + * @param machine State machine handler functions + * @param frame The current frame + * @returns The new frame + */ +function right(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { + Debug.assertEqual(stateStack[stackIndex], right); + Debug.assertIsDefined(machine.onRight); + stateStack[stackIndex] = nextState(machine, right); + const nextNode = machine.onRight(nodeStack[stackIndex].right, userStateStack[stackIndex], nodeStack[stackIndex]); + if (nextNode) { + checkCircularity(stackIndex, nodeStack, nextNode); + return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode); + } + return stackIndex; +} + +/** + * Handles walking out of a `BinaryExpression`. + * @param machine State machine handler functions + * @param frame The current frame + * @returns The new frame + */ +function exit(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }, _outerState: TOuterState): number { + Debug.assertEqual(stateStack[stackIndex], exit); + stateStack[stackIndex] = nextState(machine, exit); + const result = machine.onExit(nodeStack[stackIndex], userStateStack[stackIndex]); + if (stackIndex > 0) { + stackIndex--; + if (machine.foldState) { + const side = stateStack[stackIndex] === exit ? "right" : "left"; + userStateStack[stackIndex] = machine.foldState(userStateStack[stackIndex], result, side); + } + } + else { + resultHolder.value = result; + } + return stackIndex; +} + +/** + * Handles a frame that is already done. + * @returns The `done` state. + */ +function done(_machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], _nodeStack: BinaryExpression[], _userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { + Debug.assertEqual(stateStack[stackIndex], done); + return stackIndex; +} + +function nextState(machine: BinaryExpressionStateMachine, currentState: BinaryExpressionState) { + switch (currentState) { + case enter: + if (machine.onLeft) return left; + // falls through + case left: + if (machine.onOperator) return operator; + // falls through + case operator: + if (machine.onRight) return right; + // falls through + case right: return exit; + case exit: return done; + case done: return done; + default: Debug.fail("Invalid state"); + } +} + +function pushStack(stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], node: BinaryExpression) { + stackIndex++; + stateStack[stackIndex] = enter; + nodeStack[stackIndex] = node; + userStateStack[stackIndex] = undefined!; + return stackIndex; +} + +function checkCircularity(stackIndex: number, nodeStack: BinaryExpression[], node: BinaryExpression) { + if (Debug.shouldAssert(Debug.AssertionLevel.Aggressive)) { + while (stackIndex >= 0) { + Debug.assert(nodeStack[stackIndex] !== node, "Circular traversal detected."); + stackIndex--; + } + } +} + +/** + * Holds state machine handler functions + */ + class BinaryExpressionStateMachine { + constructor( + readonly onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, + readonly onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + readonly onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, + readonly onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + readonly onExit: (node: BinaryExpression, userState: TState) => TResult, + readonly foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, + ) { + } +} + +/** + * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree. + * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking. + * @param onLeft Callback evaluated when walking the left side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the right side. + * @param onRight Callback evaluated when walking the right side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the end of the node. + * @param onExit Callback evaluated when exiting a `BinaryExpression`. The result returned will either be folded into the parent's state, or returned from the walker if at the top frame. + * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent. + * @returns A function that walks a `BinaryExpression` node using the above callbacks, returning the result of the call to `onExit` from the outermost `BinaryExpression` node. + * + * @internal + */ + export function createBinaryExpressionTrampoline( + onEnter: (node: BinaryExpression, prev: TState | undefined) => TState, + onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, + onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + onExit: (node: BinaryExpression, userState: TState) => TResult, + foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, +): (node: BinaryExpression) => TResult; +/** + * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree. + * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking. + * @param onLeft Callback evaluated when walking the left side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the right side. + * @param onRight Callback evaluated when walking the right side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the end of the node. + * @param onExit Callback evaluated when exiting a `BinaryExpression`. The result returned will either be folded into the parent's state, or returned from the walker if at the top frame. + * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent. + * @returns A function that walks a `BinaryExpression` node using the above callbacks, returning the result of the call to `onExit` from the outermost `BinaryExpression` node. + * + * @internal + */ +export function createBinaryExpressionTrampoline( + onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, + onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, + onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + onExit: (node: BinaryExpression, userState: TState) => TResult, + foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, +): (node: BinaryExpression, outerState: TOuterState) => TResult; + +/** @internal */ +export function createBinaryExpressionTrampoline( + onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, + onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, + onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, + onExit: (node: BinaryExpression, userState: TState) => TResult, + foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, +) { + const machine = new BinaryExpressionStateMachine(onEnter, onLeft, onOperator, onRight, onExit, foldState); + return trampoline; + + function trampoline(node: BinaryExpression, outerState: TOuterState) { + const resultHolder: { value: TResult } = { value: undefined! }; + const stateStack: BinaryExpressionState[] = [enter]; + const nodeStack: BinaryExpression[] = [node]; + const userStateStack: TState[] = [undefined!]; + let stackIndex = 0; + while (stateStack[stackIndex] !== done) { + stackIndex = stateStack[stackIndex](machine, stackIndex, stateStack, nodeStack, userStateStack, resultHolder, outerState); + } + Debug.assertEqual(stackIndex, 0); + return resultHolder.value; + } +} diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index 9f0cd1d2a9d9e..fbef39839c552 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -1,142 +1,57 @@ +import { + arrayToMap, + compareValues, + memoize, +} from "../core"; +import { Comparison } from "../corePublic"; +import * as Debug from "../debug"; import { __String, ArrayLiteralExpression, - arrayToMap, BindingOrAssignmentElement, Block, - compareValues, - Comparison, - createExpressionFromEntityName, - Debug, EmitFlags, EmitHelper, + EmitHelperFactory, EmitHelperUniqueNameCallback, EmitNode, EntityName, + ESDecorateClassContext, + ESDecorateClassElementAccess, + ESDecorateClassElementContext, + ESDecorateContext, + ESDecorateName, Expression, FunctionExpression, GeneratedIdentifierFlags, - getEmitFlags, - getEmitScriptTarget, - getPropertyNameOfBindingOrAssignmentElement, Identifier, InternalEmitFlags, - isCallExpression, - isComputedPropertyName, - isIdentifier, - memoize, ObjectLiteralElementLike, - PrivateIdentifier, + PrivateIdentifierKind, ScriptTarget, - setEmitFlags, - setInternalEmitFlags, - setTextRange, SyntaxKind, TextRange, TransformationContext, UnscopedEmitHelper, -} from "../_namespaces/ts"; - -/** @internal */ -export const enum PrivateIdentifierKind { - Field = "f", - Method = "m", - Accessor = "a" -} - -/** - * Describes the decorator context object passed to a native ECMAScript decorator for a class. - * - * @internal - */ -export interface ESDecorateClassContext { - /** - * The kind of the decorated element. - */ - kind: "class"; - - /** - * The name of the decorated element. - */ - name: Expression; -} - -/** - * Describes the decorator context object passed to a native ECMAScript decorator for a class element. - * - * @internal - */ -export interface ESDecorateClassElementContext { - /** - * The kind of the decorated element. - */ - kind: "method" | "getter" | "setter" | "accessor" | "field"; - name: ESDecorateName; - static: boolean; - private: boolean; - access: ESDecorateClassElementAccess; -} - -/** @internal */ -export interface ESDecorateClassElementAccess { - get?: boolean; - set?: boolean; -} - -/** @internal */ -export type ESDecorateName = - | { computed: true, name: Expression } - | { computed: false, name: Identifier | PrivateIdentifier } - ; - -/** @internal */ -export type ESDecorateContext = - | ESDecorateClassContext - | ESDecorateClassElementContext - ; - -/** @internal */ -export interface EmitHelperFactory { - getUnscopedHelperName(name: string): Identifier; - // TypeScript Helpers - createDecorateHelper(decoratorExpressions: readonly Expression[], target: Expression, memberName?: Expression, descriptor?: Expression): Expression; - createMetadataHelper(metadataKey: string, metadataValue: Expression): Expression; - createParamHelper(expression: Expression, parameterOffset: number): Expression; - // ES Decorators Helpers - createESDecorateHelper(ctor: Expression, descriptorIn: Expression, decorators: Expression, contextIn: ESDecorateContext, initializers: Expression, extraInitializers: Expression): Expression; - createRunInitializersHelper(thisArg: Expression, initializers: Expression, value?: Expression): Expression; - // ES2018 Helpers - createAssignHelper(attributesSegments: readonly Expression[]): Expression; - createAwaitHelper(expression: Expression): Expression; - createAsyncGeneratorHelper(generatorFunc: FunctionExpression, hasLexicalThis: boolean): Expression; - createAsyncDelegatorHelper(expression: Expression): Expression; - createAsyncValuesHelper(expression: Expression): Expression; - // ES2018 Destructuring Helpers - createRestHelper(value: Expression, elements: readonly BindingOrAssignmentElement[], computedTempVariables: readonly Expression[] | undefined, location: TextRange): Expression; - // ES2017 Helpers - createAwaiterHelper(hasLexicalThis: boolean, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression | undefined, body: Block): Expression; - // ES2015 Helpers - createExtendsHelper(name: Identifier): Expression; - createTemplateObjectHelper(cooked: ArrayLiteralExpression, raw: ArrayLiteralExpression): Expression; - createSpreadArrayHelper(to: Expression, from: Expression, packFrom: boolean): Expression; - createPropKeyHelper(expr: Expression): Expression; - createSetFunctionNameHelper(f: Expression, name: Expression, prefix?: string): Expression; - // ES2015 Destructuring Helpers - createValuesHelper(expression: Expression): Expression; - createReadHelper(iteratorRecord: Expression, count: number | undefined): Expression; - // ES2015 Generator Helpers - createGeneratorHelper(body: FunctionExpression): Expression; - // ES Module Helpers - createCreateBindingHelper(module: Expression, inputName: Expression, outputName: Expression | undefined): Expression; - createImportStarHelper(expression: Expression): Expression; - createImportStarCallbackHelper(): Expression; - createImportDefaultHelper(expression: Expression): Expression; - createExportStarHelper(moduleExpression: Expression, exportsExpression?: Expression): Expression; - // Class Fields Helpers - createClassPrivateFieldGetHelper(receiver: Expression, state: Identifier, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression; - createClassPrivateFieldSetHelper(receiver: Expression, state: Identifier, value: Expression, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression; - createClassPrivateFieldInHelper(state: Identifier, receiver: Expression): Expression; -} +} from "../types"; +import { + getEmitFlags, + getEmitScriptTarget, +} from "../utilities"; +import { + setEmitFlags, + setInternalEmitFlags, +} from "./emitNode"; +import { + isCallExpression, + isComputedPropertyName, + isIdentifier, +} from "./nodeTests"; +import { + createExpressionFromEntityName, + getPropertyNameOfBindingOrAssignmentElement, +} from "./utilities"; +import { setTextRange } from "./utilitiesPublic"; /** @internal */ export function createEmitHelperFactory(context: TransformationContext): EmitHelperFactory { diff --git a/src/compiler/factory/emitNode.ts b/src/compiler/factory/emitNode.ts index fba507d899ea7..c2c8a8973015a 100644 --- a/src/compiler/factory/emitNode.ts +++ b/src/compiler/factory/emitNode.ts @@ -1,24 +1,23 @@ import { - AccessExpression, append, appendIfUnique, + orderedRemoveItem, + some, +} from "../core"; +import * as Debug from "../debug"; +import { + AccessExpression, AutoGenerateInfo, - Debug, EmitFlags, EmitHelper, EmitNode, - getParseTreeNode, - getSourceFileOfNode, Identifier, ImportSpecifier, InternalEmitFlags, - isParseTreeNode, Node, NodeArray, - orderedRemoveItem, PrivateIdentifier, SnippetElement, - some, SourceFile, SourceMapRange, SyntaxKind, @@ -26,7 +25,12 @@ import { TextRange, TypeNode, TypeParameterDeclaration, -} from "../_namespaces/ts"; +} from "../types"; +import { getSourceFileOfNode } from "../utilities"; +import { + getParseTreeNode, + isParseTreeNode, +} from "../utilitiesPublic"; /** * Associates a node with the current transformation, initializing diff --git a/src/compiler/factory/nodeConverters.ts b/src/compiler/factory/nodeConverters.ts index 1541d56bf0232..6339c56b5de62 100644 --- a/src/compiler/factory/nodeConverters.ts +++ b/src/compiler/factory/nodeConverters.ts @@ -1,3 +1,9 @@ +import { + cast, + map, + notImplemented, +} from "../core"; +import * as Debug from "../debug"; import { ArrayBindingOrAssignmentElement, ArrayBindingOrAssignmentPattern, @@ -5,34 +11,38 @@ import { BindingOrAssignmentElementTarget, BindingOrAssignmentPattern, Block, - cast, ConciseBody, - Debug, Expression, FunctionDeclaration, + NodeConverters, + NodeFactory, + ObjectBindingOrAssignmentElement, + ObjectBindingOrAssignmentPattern, + SyntaxKind, +} from "../types"; +import { getModifiers, + isBindingPattern, + isExpression, + isObjectLiteralElementLike, +} from "../utilitiesPublic"; +import { getStartsOnNewLine, + setStartsOnNewLine, +} from "./emitNode"; +import { isArrayBindingPattern, isArrayLiteralExpression, isBindingElement, - isBindingPattern, isBlock, - isExpression, isIdentifier, isObjectBindingPattern, - isObjectLiteralElementLike, isObjectLiteralExpression, - map, - NodeConverters, - NodeFactory, - notImplemented, - ObjectBindingOrAssignmentElement, - ObjectBindingOrAssignmentPattern, +} from "./nodeTests"; +import { setOriginalNode, - setStartsOnNewLine, setTextRange, - SyntaxKind, -} from "../_namespaces/ts"; +} from "./utilitiesPublic"; /** @internal */ export function createNodeConverters(factory: NodeFactory): NodeConverters { diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index c3d2e25f38e35..079466dbd8b74 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -1,8 +1,40 @@ import { - __String, addRange, append, - appendIfUnique, + cast, + emptyArray, + every, + forEach, + hasProperty, + isArray, + isString, + lastOrUndefined, + map, + memoize, + memoizeOne, + reduceLeft, + returnTrue, + sameFlatMap, + singleOrUndefined, + some, + startsWith, +} from "../core"; +import * as Debug from "../debug"; +import { getBuildInfo } from "../emitter"; +import { objectAllocator } from "../objectAllocator"; +import { parseNodeFactory } from "../parser"; +import { + setEachParent, + setParent, +} from "../parserUtilities"; +import { + createScanner, + getLineAndCharacterOfPosition, + Scanner, + stringToToken, +} from "../scanner"; +import { + __String, ArrayBindingElement, ArrayBindingPattern, ArrayLiteralExpression, @@ -17,7 +49,6 @@ import { AsteriskToken, AwaitExpression, AwaitKeyword, - BaseNodeFactory, BigIntLiteral, BinaryExpression, BinaryOperator, @@ -41,7 +72,6 @@ import { CaseBlock, CaseClause, CaseOrDefaultClause, - cast, CatchClause, ClassDeclaration, ClassElement, @@ -58,13 +88,7 @@ import { ConstructorDeclaration, ConstructorTypeNode, ConstructSignatureDeclaration, - containsObjectRestOrSpread, ContinueStatement, - createBaseNodeFactory, - createNodeConverters, - createParenthesizerRules, - createScanner, - Debug, DebuggerStatement, Declaration, DeclarationName, @@ -76,16 +100,12 @@ import { ElementAccessChain, ElementAccessExpression, EmitFlags, - EmitNode, - emptyArray, EmptyStatement, EndOfFileToken, EntityName, EnumDeclaration, EnumMember, EqualsGreaterThanToken, - escapeLeadingUnderscores, - every, ExclamationToken, ExportAssignment, ExportDeclaration, @@ -96,11 +116,8 @@ import { ExternalModuleReference, FalseLiteral, FileReference, - findUseStrictPrologue, - forEach, ForInitializer, ForInStatement, - formatGeneratedName, ForOfStatement, ForStatement, FunctionDeclaration, @@ -111,27 +128,9 @@ import { GeneratedNamePart, GeneratedPrivateIdentifier, GetAccessorDeclaration, - getAllUnscopedEmitHelpers, - getBuildInfo, - getCommentRange, - getEmitFlags, - getIdentifierTypeArguments, - getJSDocTypeAliasName, - getLineAndCharacterOfPosition, - getNameOfDeclaration, - getNodeId, - getNonAssignedNameOfDeclaration, - getSourceMapRange, - getSyntheticLeadingComments, - getSyntheticTrailingComments, - getTextOfIdentifierOrLiteral, - hasInvalidEscape, HasModifiers, - hasProperty, - hasSyntacticModifier, HeritageClause, Identifier, - idText, IfStatement, ImportClause, ImportDeclaration, @@ -144,80 +143,7 @@ import { InferTypeNode, InputFiles, InterfaceDeclaration, - InternalEmitFlags, IntersectionTypeNode, - isArray, - isArrayLiteralExpression, - isArrowFunction, - isBinaryExpression, - isCallChain, - isClassDeclaration, - isClassExpression, - isCommaListExpression, - isCommaToken, - isComputedPropertyName, - isConstructorDeclaration, - isConstructorTypeNode, - isCustomPrologue, - isElementAccessChain, - isElementAccessExpression, - isEnumDeclaration, - isExclamationToken, - isExportAssignment, - isExportDeclaration, - isExternalModuleReference, - isFunctionDeclaration, - isFunctionExpression, - isGeneratedIdentifier, - isGeneratedPrivateIdentifier, - isGetAccessorDeclaration, - isHoistedFunction, - isHoistedVariableStatement, - isIdentifier, - isImportDeclaration, - isImportEqualsDeclaration, - isImportKeyword, - isIndexSignatureDeclaration, - isInterfaceDeclaration, - isLabeledStatement, - isLocalName, - isLogicalOrCoalescingAssignmentOperator, - isMemberName, - isMethodDeclaration, - isMethodSignature, - isModuleDeclaration, - isNamedDeclaration, - isNodeArray, - isNodeKind, - isNonNullChain, - isNotEmittedStatement, - isObjectLiteralExpression, - isOmittedExpression, - isOuterExpression, - isParameter, - isParenthesizedExpression, - isParseTreeNode, - isPrivateIdentifier, - isPrologueDirective, - isPropertyAccessChain, - isPropertyAccessExpression, - isPropertyDeclaration, - isPropertyName, - isPropertySignature, - isQuestionToken, - isSetAccessorDeclaration, - isSourceFile, - isStatement, - isStatementOrBlock, - isString, - isStringLiteral, - isSuperKeyword, - isSuperProperty, - isThisIdentifier, - isTypeAliasDeclaration, - isTypeParameterDeclaration, - isVariableDeclaration, - isVariableStatement, JSDoc, JSDocAllType, JSDocAugmentsTag, @@ -289,15 +215,11 @@ import { KeywordTypeSyntaxKind, LabeledStatement, LanguageVariant, - lastOrUndefined, LeftHandSideExpression, LiteralToken, LiteralTypeNode, - map, MappedTypeNode, MemberName, - memoize, - memoizeOne, MetaProperty, MethodDeclaration, MethodSignature, @@ -306,7 +228,6 @@ import { Modifier, ModifierFlags, ModifierLike, - modifiersToFlags, ModifierSyntaxKind, ModifierToken, ModuleBlock, @@ -330,16 +251,12 @@ import { NodeArray, NodeFactory, NodeFlags, - nodeIsSynthesized, NonNullChain, NonNullExpression, NoSubstitutionTemplateLiteral, NotEmittedStatement, NullLiteral, - nullNodeConverters, - nullParenthesizerRules, NumericLiteral, - objectAllocator, ObjectBindingPattern, ObjectLiteralElementLike, ObjectLiteralExpression, @@ -350,7 +267,6 @@ import { ParameterDeclaration, ParenthesizedExpression, ParenthesizedTypeNode, - parseNodeFactory, PartiallyEmittedExpression, Path, PlusToken, @@ -370,7 +286,6 @@ import { PropertyNameLiteral, PropertySignature, PseudoBigInt, - pseudoBigIntToString, PunctuationSyntaxKind, PunctuationToken, QualifiedName, @@ -378,39 +293,21 @@ import { QuestionToken, ReadonlyKeyword, RedirectInfo, - reduceLeft, RegularExpressionLiteral, RestTypeNode, ReturnStatement, - returnTrue, - sameFlatMap, SatisfiesExpression, - Scanner, ScriptTarget, SemicolonClassElement, SetAccessorDeclaration, - setEachParent, - setEmitFlags, - setIdentifierAutoGenerate, - setIdentifierTypeArguments, - setParent, - setTextRange, - setTextRangePosWidth, ShorthandPropertyAssignment, SignatureDeclarationBase, - singleOrUndefined, - skipOuterExpressions, - skipParentheses, - some, SourceFile, SourceMapSource, SpreadAssignment, SpreadElement, - startOnNewLine, - startsWith, Statement, StringLiteral, - stringToToken, SuperExpression, SwitchStatement, SyntaxKind, @@ -428,7 +325,6 @@ import { TemplateMiddle, TemplateSpan, TemplateTail, - TextRange, ThisExpression, ThisTypeNode, ThrowStatement, @@ -464,13 +360,138 @@ import { VariableDeclaration, VariableDeclarationList, VariableStatement, - visitNode, VisitResult, VoidExpression, WhileStatement, WithStatement, YieldExpression, -} from "../_namespaces/ts"; +} from "../types"; +import { + findUseStrictPrologue, + getEmitFlags, + getNodeId, + getTextOfIdentifierOrLiteral, + hasInvalidEscape, + hasSyntacticModifier, + isCustomPrologue, + isHoistedFunction, + isHoistedVariableStatement, + isLogicalOrCoalescingAssignmentOperator, + isOuterExpression, + isPrologueDirective, + isSuperProperty, + isThisIdentifier, + modifiersToFlags, + nodeIsSynthesized, + pseudoBigIntToString, + setTextRangePosWidth, + skipOuterExpressions, + skipParentheses, +} from "../utilities"; +import { + escapeLeadingUnderscores, + getNameOfDeclaration, + getNonAssignedNameOfDeclaration, + idText, + isCallChain, + isElementAccessChain, + isGeneratedIdentifier, + isGeneratedPrivateIdentifier, + isMemberName, + isNamedDeclaration, + isNodeArray, + isNodeKind, + isNonNullChain, + isParseTreeNode, + isPropertyAccessChain, + isPropertyName, + isStatement, + isStatementOrBlock, +} from "../utilitiesPublic"; +import { visitNode } from "../visitorPublic"; +import { + BaseNodeFactory, + createBaseNodeFactory, +} from "./baseNodeFactory"; +import { getAllUnscopedEmitHelpers } from "./emitHelpers"; +import { + getCommentRange, + getIdentifierTypeArguments, + getSourceMapRange, + getSyntheticLeadingComments, + getSyntheticTrailingComments, + setEmitFlags, + setIdentifierAutoGenerate, + setIdentifierTypeArguments, +} from "./emitNode"; +import { + createNodeConverters, + nullNodeConverters, +} from "./nodeConverters"; +import { + isArrayLiteralExpression, + isArrowFunction, + isBinaryExpression, + isClassDeclaration, + isClassExpression, + isCommaListExpression, + isCommaToken, + isComputedPropertyName, + isConstructorDeclaration, + isConstructorTypeNode, + isElementAccessExpression, + isEnumDeclaration, + isExclamationToken, + isExportAssignment, + isExportDeclaration, + isExternalModuleReference, + isFunctionDeclaration, + isFunctionExpression, + isGetAccessorDeclaration, + isIdentifier, + isImportDeclaration, + isImportEqualsDeclaration, + isImportKeyword, + isIndexSignatureDeclaration, + isInterfaceDeclaration, + isLabeledStatement, + isMethodDeclaration, + isMethodSignature, + isModuleDeclaration, + isNotEmittedStatement, + isObjectLiteralExpression, + isOmittedExpression, + isParameter, + isParenthesizedExpression, + isPrivateIdentifier, + isPropertyAccessExpression, + isPropertyDeclaration, + isPropertySignature, + isQuestionToken, + isSetAccessorDeclaration, + isSourceFile, + isStringLiteral, + isSuperKeyword, + isTypeAliasDeclaration, + isTypeParameterDeclaration, + isVariableDeclaration, + isVariableStatement, +} from "./nodeTests"; +import { + createParenthesizerRules, + nullParenthesizerRules, +} from "./parenthesizerRules"; +import { + containsObjectRestOrSpread, + formatGeneratedName, + getJSDocTypeAliasName, + isLocalName, + startOnNewLine, +} from "./utilities"; +import { + setOriginalNode, + setTextRange, +} from "./utilitiesPublic"; let nextAutoGenerateId = 0; @@ -7567,56 +7588,3 @@ let SourceMapSource: new (fileName: string, text: string, skipTrivia?: (pos: num export function createSourceMapSource(fileName: string, text: string, skipTrivia?: (pos: number) => number): SourceMapSource { return new (SourceMapSource || (SourceMapSource = objectAllocator.getSourceMapSourceConstructor()))(fileName, text, skipTrivia); } - -// Utilities - -export function setOriginalNode(node: T, original: Node | undefined): T { - node.original = original; - if (original) { - const emitNode = original.emitNode; - if (emitNode) node.emitNode = mergeEmitNode(emitNode, node.emitNode); - } - return node; -} - -function mergeEmitNode(sourceEmitNode: EmitNode, destEmitNode: EmitNode | undefined) { - const { - flags, - internalFlags, - leadingComments, - trailingComments, - commentRange, - sourceMapRange, - tokenSourceMapRanges, - constantValue, - helpers, - startsOnNewLine, - snippetElement, - } = sourceEmitNode; - if (!destEmitNode) destEmitNode = {} as EmitNode; - // We are using `.slice()` here in case `destEmitNode.leadingComments` is pushed to later. - if (leadingComments) destEmitNode.leadingComments = addRange(leadingComments.slice(), destEmitNode.leadingComments); - if (trailingComments) destEmitNode.trailingComments = addRange(trailingComments.slice(), destEmitNode.trailingComments); - if (flags) destEmitNode.flags = flags; - if (internalFlags) destEmitNode.internalFlags = internalFlags & ~InternalEmitFlags.Immutable; - if (commentRange) destEmitNode.commentRange = commentRange; - if (sourceMapRange) destEmitNode.sourceMapRange = sourceMapRange; - if (tokenSourceMapRanges) destEmitNode.tokenSourceMapRanges = mergeTokenSourceMapRanges(tokenSourceMapRanges, destEmitNode.tokenSourceMapRanges!); - if (constantValue !== undefined) destEmitNode.constantValue = constantValue; - if (helpers) { - for (const helper of helpers) { - destEmitNode.helpers = appendIfUnique(destEmitNode.helpers, helper); - } - } - if (startsOnNewLine !== undefined) destEmitNode.startsOnNewLine = startsOnNewLine; - if (snippetElement !== undefined) destEmitNode.snippetElement = snippetElement; - return destEmitNode; -} - -function mergeTokenSourceMapRanges(sourceRanges: (TextRange | undefined)[], destRanges: (TextRange | undefined)[]) { - if (!destRanges) destRanges = []; - for (const key in sourceRanges) { - destRanges[key] = sourceRanges[key]; - } - return destRanges; -} diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index ffc8c4a44839d..c91b666bb03ca 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -226,7 +226,7 @@ import { WhileStatement, WithStatement, YieldExpression, -} from "../_namespaces/ts"; +} from "../types"; // Literals diff --git a/src/compiler/factory/parenthesizerRules.ts b/src/compiler/factory/parenthesizerRules.ts index 5a1386d567b3f..72bf7c7a77dc4 100644 --- a/src/compiler/factory/parenthesizerRules.ts +++ b/src/compiler/factory/parenthesizerRules.ts @@ -1,54 +1,62 @@ import { - Associativity, - BinaryExpression, - BinaryOperator, cast, compareValues, - Comparison, + identity, + last, + sameMap, + some, +} from "../core"; +import { Comparison } from "../corePublic"; +import { + BinaryExpression, + BinaryOperator, ConciseBody, Expression, + LeftHandSideExpression, + NamedTupleMember, + NewExpression, + NodeArray, + NodeFactory, + OuterExpressionKinds, + ParenthesizerRules, + SyntaxKind, + TypeNode, + UnaryExpression, +} from "../types"; +import { + Associativity, getExpressionAssociativity, getExpressionPrecedence, getLeftmostExpression, getOperatorAssociativity, getOperatorPrecedence, - identity, + OperatorPrecedence, +} from "../utilities"; +import { + isFunctionOrConstructorTypeNode, + isLeftHandSideExpression, + isLiteralKind, + isNodeArray, + isOptionalChain, + isUnaryExpression, + skipPartiallyEmittedExpressions, +} from "../utilitiesPublic"; +import { isBinaryExpression, isBlock, isCallExpression, - isCommaSequence, isConditionalTypeNode, isConstructorTypeNode, - isFunctionOrConstructorTypeNode, isFunctionTypeNode, isInferTypeNode, isIntersectionTypeNode, isJSDocNullableType, - isLeftHandSideExpression, - isLiteralKind, isNamedTupleMember, - isNodeArray, - isOptionalChain, isTypeOperatorNode, - isUnaryExpression, isUnionTypeNode, - last, - LeftHandSideExpression, - NamedTupleMember, - NewExpression, - NodeArray, - NodeFactory, - OperatorPrecedence, - OuterExpressionKinds, - ParenthesizerRules, - sameMap, - setTextRange, - skipPartiallyEmittedExpressions, - some, - SyntaxKind, - TypeNode, - UnaryExpression, -} from "../_namespaces/ts"; +} from "./nodeTests"; +import { isCommaSequence } from "./utilities"; +import { setTextRange } from "./utilitiesPublic"; /** @internal */ export function createParenthesizerRules(factory: NodeFactory): ParenthesizerRules { diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts index 2e2721eadcaaf..e13eb5c3cb5fd 100644 --- a/src/compiler/factory/utilities.ts +++ b/src/compiler/factory/utilities.ts @@ -1,10 +1,20 @@ +import { + compareStringsCaseSensitive, + first, + last, + map, + pushIfUnique, + some, +} from "../core"; +import * as Debug from "../debug"; +import { getExternalModuleNameFromPath } from "../moduleNameResolverUtilities"; +import { parseNodeFactory } from "../parser"; +import { setParent } from "../parserUtilities"; +import { isIdentifierText } from "../scanner"; import { AccessorDeclaration, - addEmitFlags, - addInternalEmitFlags, AdditiveOperator, AdditiveOperatorOrHigher, - AssertionLevel, AssignmentExpression, AssignmentOperatorOrHigher, AssignmentPattern, @@ -21,10 +31,8 @@ import { BooleanLiteral, CharacterCodes, CommaListExpression, - compareStringsCaseSensitive, CompilerOptions, ComputedPropertyName, - Debug, Declaration, DefaultKeyword, EmitFlags, @@ -40,90 +48,27 @@ import { ExportDeclaration, ExportKeyword, Expression, - ExpressionStatement, - externalHelpersModuleNameText, - first, - firstOrUndefined, ForInitializer, GeneratedIdentifier, GeneratedIdentifierFlags, GeneratedNamePart, GeneratedPrivateIdentifier, GetAccessorDeclaration, - getAllAccessorDeclarations, - getEmitFlags, - getEmitHelpers, - getEmitModuleKind, - getESModuleInterop, - getExternalModuleName, - getExternalModuleNameFromPath, - getJSDocType, - getJSDocTypeTag, - getModifiers, - getNamespaceDeclarationNode, - getOrCreateEmitNode, - getOriginalNode, - getParseTreeNode, - getSourceTextOfNodeFromSourceFile, - HasIllegalDecorators, HasIllegalModifiers, HasIllegalType, - HasIllegalTypeParameters, Identifier, - idText, ImportCall, ImportDeclaration, ImportEqualsDeclaration, InternalEmitFlags, - isAssignmentExpression, - isAssignmentOperator, - isAssignmentPattern, - isBlock, - isCommaListExpression, - isComputedPropertyName, - isDeclarationBindingElement, - isDefaultImport, - isEffectiveExternalModule, - isExclamationToken, - isExportNamespaceAsDefaultDeclaration, - isFileLevelUniqueName, - isGeneratedIdentifier, - isGeneratedPrivateIdentifier, - isIdentifier, - isInJSFile, - isLiteralExpression, - isMemberName, - isMinusToken, - isModifierKind, - isObjectLiteralElementLike, - isParenthesizedExpression, - isPlusToken, - isPostfixUnaryExpression, - isPrefixUnaryExpression, - isPrivateIdentifier, - isPrologueDirective, - isPropertyAssignment, - isPropertyName, - isQualifiedName, - isQuestionToken, - isReadonlyKeyword, - isShorthandPropertyAssignment, - isSourceFile, - isSpreadAssignment, - isSpreadElement, - isStringLiteral, - isThisTypeNode, - isVariableDeclarationList, JSDocNamespaceBody, JSDocTypeAssertion, JsxOpeningFragment, JsxOpeningLikeElement, - last, LeftHandSideExpression, LiteralExpression, LogicalOperator, LogicalOperatorOrHigher, - map, MemberExpression, MethodDeclaration, MinusToken, @@ -138,16 +83,11 @@ import { Node, NodeArray, NodeFactory, - nodeIsSynthesized, NullLiteral, NumericLiteral, ObjectLiteralElementLike, ObjectLiteralExpression, - OuterExpression, - OuterExpressionKinds, - outFile, ParenthesizedExpression, - parseNodeFactory, PlusToken, PostfixUnaryExpression, PrefixUnaryExpression, @@ -155,20 +95,15 @@ import { PropertyAssignment, PropertyDeclaration, PropertyName, - pushIfUnique, QuestionToken, ReadonlyKeyword, RelationalOperator, RelationalOperatorOrHigher, + ScriptTarget, SetAccessorDeclaration, - setOriginalNode, - setParent, - setStartsOnNewLine, - setTextRange, ShiftOperator, ShiftOperatorOrHigher, ShorthandPropertyAssignment, - some, SourceFile, Statement, StringLiteral, @@ -178,7 +113,78 @@ import { Token, TransformFlags, TypeNode, -} from "../_namespaces/ts"; +} from "../types"; +import { + externalHelpersModuleNameText, + getAllAccessorDeclarations, + getEmitFlags, + getEmitModuleKind, + getESModuleInterop, + getExternalModuleName, + getNamespaceDeclarationNode, + getSourceTextOfNodeFromSourceFile, + isAssignmentExpression, + isAssignmentOperator, + isDefaultImport, + isEffectiveExternalModule, + isExportNamespaceAsDefaultDeclaration, + isFileLevelUniqueName, + isNumericLiteralName, + nodeIsSynthesized, + outFile, + skipOuterExpressions, +} from "../utilities"; +import { + getJSDocType, + getModifiers, + getOriginalNode, + getParseTreeNode, + idText, + isAssignmentPattern, + isDeclarationBindingElement, + isGeneratedIdentifier, + isGeneratedPrivateIdentifier, + isLiteralExpression, + isMemberName, + isModifierKind, + isObjectLiteralElementLike, + isPropertyName, +} from "../utilitiesPublic"; +import { + addEmitFlags, + addInternalEmitFlags, + getEmitHelpers, + getOrCreateEmitNode, + setStartsOnNewLine, +} from "./emitNode"; +import { + isBlock, + isCommaListExpression, + isComputedPropertyName, + isExclamationToken, + isIdentifier, + isMinusToken, + isParenthesizedExpression, + isPlusToken, + isPostfixUnaryExpression, + isPrefixUnaryExpression, + isPrivateIdentifier, + isPropertyAssignment, + isQualifiedName, + isQuestionToken, + isReadonlyKeyword, + isShorthandPropertyAssignment, + isSourceFile, + isSpreadAssignment, + isSpreadElement, + isStringLiteral, + isThisTypeNode, + isVariableDeclarationList, +} from "./nodeTests"; +import { + setOriginalNode, + setTextRange, +} from "./utilitiesPublic"; // Compound nodes @@ -583,33 +589,6 @@ export function isExportName(node: Identifier) { return (getEmitFlags(node) & EmitFlags.ExportName) !== 0; } -function isUseStrictPrologue(node: ExpressionStatement): boolean { - return isStringLiteral(node.expression) && node.expression.text === "use strict"; -} - -/** @internal */ -export function findUseStrictPrologue(statements: readonly Statement[]): Statement | undefined { - for (const statement of statements) { - if (isPrologueDirective(statement)) { - if (isUseStrictPrologue(statement)) { - return statement; - } - } - else { - break; - } - } - return undefined; -} - -/** @internal */ -export function startsWithUseStrict(statements: readonly Statement[]) { - const firstStatement = firstOrUndefined(statements); - return firstStatement !== undefined - && isPrologueDirective(firstStatement) - && isUseStrictPrologue(firstStatement); -} - /** @internal */ export function isCommaExpression(node: Expression): node is BinaryExpression & { operatorToken: Token } { return node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.CommaToken; @@ -620,13 +599,6 @@ export function isCommaSequence(node: Expression): node is BinaryExpression & {o return isCommaExpression(node) || isCommaListExpression(node); } -/** @internal */ -export function isJSDocTypeAssertion(node: Node): node is JSDocTypeAssertion { - return isParenthesizedExpression(node) - && isInJSFile(node) - && !!getJSDocTypeTag(node); -} - /** @internal */ export function getJSDocTypeAssertionType(node: JSDocTypeAssertion): TypeNode { const type = getJSDocType(node); @@ -634,58 +606,6 @@ export function getJSDocTypeAssertionType(node: JSDocTypeAssertion): TypeNode { return type; } -/** @internal */ -export function isOuterExpression(node: Node, kinds = OuterExpressionKinds.All): node is OuterExpression { - switch (node.kind) { - case SyntaxKind.ParenthesizedExpression: - if (kinds & OuterExpressionKinds.ExcludeJSDocTypeAssertion && isJSDocTypeAssertion(node)) { - return false; - } - return (kinds & OuterExpressionKinds.Parentheses) !== 0; - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - case SyntaxKind.ExpressionWithTypeArguments: - case SyntaxKind.SatisfiesExpression: - return (kinds & OuterExpressionKinds.TypeAssertions) !== 0; - case SyntaxKind.NonNullExpression: - return (kinds & OuterExpressionKinds.NonNullAssertions) !== 0; - case SyntaxKind.PartiallyEmittedExpression: - return (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) !== 0; - } - return false; -} - -/** @internal */ -export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; -/** @internal */ -export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; -/** @internal */ -export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { - while (isOuterExpression(node, kinds)) { - node = node.expression; - } - return node; -} - -/** @internal */ -export function walkUpOuterExpressions(node: Expression, kinds = OuterExpressionKinds.All): Node { - let parent = node.parent; - while (isOuterExpression(parent, kinds)) { - parent = parent.parent; - Debug.assert(parent); - } - return parent; -} - -/** @internal */ -export function skipAssertions(node: Expression): Expression; -/** @internal */ -export function skipAssertions(node: Node): Node; -/** @internal */ -export function skipAssertions(node: Node): Node { - return skipOuterExpressions(node, OuterExpressionKinds.Assertions); -} - /** @internal */ export function startOnNewLine(node: T): T { return setStartsOnNewLine(node, /*newLine*/ true); @@ -1118,36 +1038,6 @@ export function canHaveIllegalType(node: Node): node is HasIllegalType { || kind === SyntaxKind.SetAccessor; } -/** @internal */ -export function canHaveIllegalTypeParameters(node: Node): node is HasIllegalTypeParameters { - const kind = node.kind; - return kind === SyntaxKind.Constructor - || kind === SyntaxKind.GetAccessor - || kind === SyntaxKind.SetAccessor; -} - -/** @internal */ -export function canHaveIllegalDecorators(node: Node): node is HasIllegalDecorators { - const kind = node.kind; - return kind === SyntaxKind.PropertyAssignment - || kind === SyntaxKind.ShorthandPropertyAssignment - || kind === SyntaxKind.FunctionDeclaration - || kind === SyntaxKind.Constructor - || kind === SyntaxKind.IndexSignature - || kind === SyntaxKind.ClassStaticBlockDeclaration - || kind === SyntaxKind.MissingDeclaration - || kind === SyntaxKind.VariableStatement - || kind === SyntaxKind.InterfaceDeclaration - || kind === SyntaxKind.TypeAliasDeclaration - || kind === SyntaxKind.EnumDeclaration - || kind === SyntaxKind.ModuleDeclaration - || kind === SyntaxKind.ImportEqualsDeclaration - || kind === SyntaxKind.ImportDeclaration - || kind === SyntaxKind.NamespaceExportDeclaration - || kind === SyntaxKind.ExportDeclaration - || kind === SyntaxKind.ExportAssignment; -} - /** @internal */ export function canHaveIllegalModifiers(node: Node): node is HasIllegalModifiers { const kind = node.kind; @@ -1287,220 +1177,6 @@ export function isBinaryOperatorToken(node: Node): node is BinaryOperatorToken { return isBinaryOperator(node.kind); } -type BinaryExpressionState = (machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }, outerState: TOuterState) => number; - -namespace BinaryExpressionState { - /** - * Handles walking into a `BinaryExpression`. - * @param machine State machine handler functions - * @param frame The current frame - * @returns The new frame - */ - export function enter(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, outerState: TOuterState): number { - const prevUserState = stackIndex > 0 ? userStateStack[stackIndex - 1] : undefined; - Debug.assertEqual(stateStack[stackIndex], enter); - userStateStack[stackIndex] = machine.onEnter(nodeStack[stackIndex], prevUserState, outerState); - stateStack[stackIndex] = nextState(machine, enter); - return stackIndex; - } - - /** - * Handles walking the `left` side of a `BinaryExpression`. - * @param machine State machine handler functions - * @param frame The current frame - * @returns The new frame - */ - export function left(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { - Debug.assertEqual(stateStack[stackIndex], left); - Debug.assertIsDefined(machine.onLeft); - stateStack[stackIndex] = nextState(machine, left); - const nextNode = machine.onLeft(nodeStack[stackIndex].left, userStateStack[stackIndex], nodeStack[stackIndex]); - if (nextNode) { - checkCircularity(stackIndex, nodeStack, nextNode); - return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode); - } - return stackIndex; - } - - /** - * Handles walking the `operatorToken` of a `BinaryExpression`. - * @param machine State machine handler functions - * @param frame The current frame - * @returns The new frame - */ - export function operator(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { - Debug.assertEqual(stateStack[stackIndex], operator); - Debug.assertIsDefined(machine.onOperator); - stateStack[stackIndex] = nextState(machine, operator); - machine.onOperator(nodeStack[stackIndex].operatorToken, userStateStack[stackIndex], nodeStack[stackIndex]); - return stackIndex; - } - - /** - * Handles walking the `right` side of a `BinaryExpression`. - * @param machine State machine handler functions - * @param frame The current frame - * @returns The new frame - */ - export function right(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { - Debug.assertEqual(stateStack[stackIndex], right); - Debug.assertIsDefined(machine.onRight); - stateStack[stackIndex] = nextState(machine, right); - const nextNode = machine.onRight(nodeStack[stackIndex].right, userStateStack[stackIndex], nodeStack[stackIndex]); - if (nextNode) { - checkCircularity(stackIndex, nodeStack, nextNode); - return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode); - } - return stackIndex; - } - - /** - * Handles walking out of a `BinaryExpression`. - * @param machine State machine handler functions - * @param frame The current frame - * @returns The new frame - */ - export function exit(machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }, _outerState: TOuterState): number { - Debug.assertEqual(stateStack[stackIndex], exit); - stateStack[stackIndex] = nextState(machine, exit); - const result = machine.onExit(nodeStack[stackIndex], userStateStack[stackIndex]); - if (stackIndex > 0) { - stackIndex--; - if (machine.foldState) { - const side = stateStack[stackIndex] === exit ? "right" : "left"; - userStateStack[stackIndex] = machine.foldState(userStateStack[stackIndex], result, side); - } - } - else { - resultHolder.value = result; - } - return stackIndex; - } - - /** - * Handles a frame that is already done. - * @returns The `done` state. - */ - export function done(_machine: BinaryExpressionStateMachine, stackIndex: number, stateStack: BinaryExpressionState[], _nodeStack: BinaryExpression[], _userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number { - Debug.assertEqual(stateStack[stackIndex], done); - return stackIndex; - } - - export function nextState(machine: BinaryExpressionStateMachine, currentState: BinaryExpressionState) { - switch (currentState) { - case enter: - if (machine.onLeft) return left; - // falls through - case left: - if (machine.onOperator) return operator; - // falls through - case operator: - if (machine.onRight) return right; - // falls through - case right: return exit; - case exit: return done; - case done: return done; - default: Debug.fail("Invalid state"); - } - } - - function pushStack(stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], node: BinaryExpression) { - stackIndex++; - stateStack[stackIndex] = enter; - nodeStack[stackIndex] = node; - userStateStack[stackIndex] = undefined!; - return stackIndex; - } - - function checkCircularity(stackIndex: number, nodeStack: BinaryExpression[], node: BinaryExpression) { - if (Debug.shouldAssert(AssertionLevel.Aggressive)) { - while (stackIndex >= 0) { - Debug.assert(nodeStack[stackIndex] !== node, "Circular traversal detected."); - stackIndex--; - } - } - } -} - -/** - * Holds state machine handler functions - */ -class BinaryExpressionStateMachine { - constructor( - readonly onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, - readonly onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, - readonly onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, - readonly onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, - readonly onExit: (node: BinaryExpression, userState: TState) => TResult, - readonly foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, - ) { - } -} - -/** - * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree. - * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking. - * @param onLeft Callback evaluated when walking the left side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the right side. - * @param onRight Callback evaluated when walking the right side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the end of the node. - * @param onExit Callback evaluated when exiting a `BinaryExpression`. The result returned will either be folded into the parent's state, or returned from the walker if at the top frame. - * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent. - * @returns A function that walks a `BinaryExpression` node using the above callbacks, returning the result of the call to `onExit` from the outermost `BinaryExpression` node. - * - * @internal - */ - export function createBinaryExpressionTrampoline( - onEnter: (node: BinaryExpression, prev: TState | undefined) => TState, - onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, - onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, - onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, - onExit: (node: BinaryExpression, userState: TState) => TResult, - foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, -): (node: BinaryExpression) => TResult; -/** - * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree. - * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking. - * @param onLeft Callback evaluated when walking the left side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the right side. - * @param onRight Callback evaluated when walking the right side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the end of the node. - * @param onExit Callback evaluated when exiting a `BinaryExpression`. The result returned will either be folded into the parent's state, or returned from the walker if at the top frame. - * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent. - * @returns A function that walks a `BinaryExpression` node using the above callbacks, returning the result of the call to `onExit` from the outermost `BinaryExpression` node. - * - * @internal - */ -export function createBinaryExpressionTrampoline( - onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, - onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, - onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, - onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, - onExit: (node: BinaryExpression, userState: TState) => TResult, - foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, -): (node: BinaryExpression, outerState: TOuterState) => TResult; -/** @internal */ -export function createBinaryExpressionTrampoline( - onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState, - onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, - onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined, - onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined, - onExit: (node: BinaryExpression, userState: TState) => TResult, - foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined, -) { - const machine = new BinaryExpressionStateMachine(onEnter, onLeft, onOperator, onRight, onExit, foldState); - return trampoline; - - function trampoline(node: BinaryExpression, outerState: TOuterState) { - const resultHolder: { value: TResult } = { value: undefined! }; - const stateStack: BinaryExpressionState[] = [BinaryExpressionState.enter]; - const nodeStack: BinaryExpression[] = [node]; - const userStateStack: TState[] = [undefined!]; - let stackIndex = 0; - while (stateStack[stackIndex] !== BinaryExpressionState.done) { - stackIndex = stateStack[stackIndex](machine, stackIndex, stateStack, nodeStack, userStateStack, resultHolder, outerState); - } - Debug.assertEqual(stackIndex, 0); - return resultHolder.value; - } -} - function isExportOrDefaultKeywordKind(kind: SyntaxKind): kind is SyntaxKind.ExportKeyword | SyntaxKind.DefaultKeyword { return kind === SyntaxKind.ExportKeyword || kind === SyntaxKind.DefaultKeyword; } @@ -1664,7 +1340,7 @@ export function createAccessorPropertyGetRedirector(factory: NodeFactory, node: * * @internal */ -export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName) { +export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName): SetAccessorDeclaration { return factory.createSetAccessorDeclaration( modifiers, name, @@ -1687,6 +1363,13 @@ export function createAccessorPropertySetRedirector(factory: NodeFactory, node: ); } +/** @internal */ +export function createPropertyNameNodeForIdentifierOrLiteral(factory: NodeFactory, name: string, target: ScriptTarget, singleQuote?: boolean, stringNamed?: boolean) { + return isIdentifierText(name, target) ? factory.createIdentifier(name) : + !stringNamed && isNumericLiteralName(name) && +name >= 0 ? factory.createNumericLiteral(+name) : + factory.createStringLiteral(name, !!singleQuote); +} + /** @internal */ export function findComputedPropertyNameCacheAssignment(name: ComputedPropertyName) { let node = name.expression; @@ -1711,9 +1394,7 @@ export function findComputedPropertyNameCacheAssignment(name: ComputedPropertyNa } function isSyntheticParenthesizedExpression(node: Expression): node is ParenthesizedExpression { - return isParenthesizedExpression(node) - && nodeIsSynthesized(node) - && !node.emitNode; + return isParenthesizedExpression(node) && nodeIsSynthesized(node) && !node.emitNode; } function flattenCommaListWorker(node: Expression, expressions: Expression[]) { diff --git a/src/compiler/factory/utilitiesPublic.ts b/src/compiler/factory/utilitiesPublic.ts index 8afd72574c0fb..d4d2b9348575b 100644 --- a/src/compiler/factory/utilitiesPublic.ts +++ b/src/compiler/factory/utilitiesPublic.ts @@ -1,11 +1,17 @@ import { + addRange, + appendIfUnique, +} from "../core"; +import { + EmitNode, HasDecorators, HasModifiers, + InternalEmitFlags, Node, - setTextRangePosEnd, SyntaxKind, TextRange, -} from "../_namespaces/ts"; +} from "../types"; +import { setTextRangePosEnd } from "../utilities"; export function setTextRange(range: T, location: TextRange | undefined): T { return location ? setTextRangePosEnd(range, location.pos, location.end) : range; @@ -49,4 +55,55 @@ export function canHaveDecorators(node: Node): node is HasDecorators { || kind === SyntaxKind.SetAccessor || kind === SyntaxKind.ClassExpression || kind === SyntaxKind.ClassDeclaration; -} \ No newline at end of file +} + +export function setOriginalNode(node: T, original: Node | undefined): T { + node.original = original; + if (original) { + const emitNode = original.emitNode; + if (emitNode) node.emitNode = mergeEmitNode(emitNode, node.emitNode); + } + return node; +} + +function mergeEmitNode(sourceEmitNode: EmitNode, destEmitNode: EmitNode | undefined) { + const { + flags, + internalFlags, + leadingComments, + trailingComments, + commentRange, + sourceMapRange, + tokenSourceMapRanges, + constantValue, + helpers, + startsOnNewLine, + snippetElement, + } = sourceEmitNode; + if (!destEmitNode) destEmitNode = {} as EmitNode; + // We are using `.slice()` here in case `destEmitNode.leadingComments` is pushed to later. + if (leadingComments) destEmitNode.leadingComments = addRange(leadingComments.slice(), destEmitNode.leadingComments); + if (trailingComments) destEmitNode.trailingComments = addRange(trailingComments.slice(), destEmitNode.trailingComments); + if (flags) destEmitNode.flags = flags; + if (internalFlags) destEmitNode.internalFlags = internalFlags & ~InternalEmitFlags.Immutable; + if (commentRange) destEmitNode.commentRange = commentRange; + if (sourceMapRange) destEmitNode.sourceMapRange = sourceMapRange; + if (tokenSourceMapRanges) destEmitNode.tokenSourceMapRanges = mergeTokenSourceMapRanges(tokenSourceMapRanges, destEmitNode.tokenSourceMapRanges!); + if (constantValue !== undefined) destEmitNode.constantValue = constantValue; + if (helpers) { + for (const helper of helpers) { + destEmitNode.helpers = appendIfUnique(destEmitNode.helpers, helper); + } + } + if (startsOnNewLine !== undefined) destEmitNode.startsOnNewLine = startsOnNewLine; + if (snippetElement !== undefined) destEmitNode.snippetElement = snippetElement; + return destEmitNode; +} + +function mergeTokenSourceMapRanges(sourceRanges: (TextRange | undefined)[], destRanges: (TextRange | undefined)[]) { + if (!destRanges) destRanges = []; + for (const key in sourceRanges) { + destRanges[key] = sourceRanges[key]; + } + return destRanges; +} diff --git a/src/compiler/fileMatcher.ts b/src/compiler/fileMatcher.ts new file mode 100644 index 0000000000000..09998d4afc3e6 --- /dev/null +++ b/src/compiler/fileMatcher.ts @@ -0,0 +1,344 @@ +import { compareStringsCaseSensitive, createGetCanonicalFileName, emptyArray, every, findIndex, flatMap, flatten, getStringComparer, indexOfAnyCharCode, last, map, sort } from "./core"; +import { combinePaths, containsPath, directorySeparator, fileExtensionIsOneOf, getDirectoryPath, getNormalizedPathComponents, hasExtension, isRootedDiskPath, normalizePath, removeTrailingDirectorySeparator } from "./path"; +import { CharacterCodes } from "./types"; + +/** @internal */ +export const commonPackageFolders: readonly string[] = ["node_modules", "bower_components", "jspm_packages"]; + +// Reserved characters, forces escaping of any non-word (or digit), non-whitespace character. +// It may be inefficient (we could just match (/[-[\]{}()*+?.,\\^$|#\s]/g), but this is future +// proof. +const reservedCharacterPattern = /[^\w\s\/]/g; +const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`; +const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question]; + +interface WildcardMatcher { + singleAsteriskRegexFragment: string; + doubleAsteriskRegexFragment: string; + replaceWildcardCharacter: (match: string) => string; +} + +const filesMatcher: WildcardMatcher = { + /** + * Matches any single directory segment unless it is the last segment and a .min.js file + * Breakdown: + * [^./] # matches everything up to the first . character (excluding directory separators) + * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension + */ + singleAsteriskRegexFragment: "([^./]|(\\.(?!min\\.js$))?)*", + /** + * Regex for the ** wildcard. Matches any number of subdirectories. When used for including + * files or directories, does not match subdirectories that start with a . character + */ + doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`, + replaceWildcardCharacter: match => replaceWildcardCharacter(match, filesMatcher.singleAsteriskRegexFragment) +}; + +const directoriesMatcher: WildcardMatcher = { + singleAsteriskRegexFragment: "[^/]*", + /** + * Regex for the ** wildcard. Matches any number of subdirectories. When used for including + * files or directories, does not match subdirectories that start with a . character + */ + doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`, + replaceWildcardCharacter: match => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment) +}; + +const excludeMatcher: WildcardMatcher = { + singleAsteriskRegexFragment: "[^/]*", + doubleAsteriskRegexFragment: "(/.+?)?", + replaceWildcardCharacter: match => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment) +}; + +const wildcardMatchers = { + files: filesMatcher, + directories: directoriesMatcher, + exclude: excludeMatcher +}; + +/** @internal */ +export const emptyFileSystemEntries: FileSystemEntries = { + files: emptyArray, + directories: emptyArray +}; + +/** @internal */ +export interface FileMatcherPatterns { + /** One pattern for each "include" spec. */ + includeFilePatterns: readonly string[] | undefined; + /** One pattern matching one of any of the "include" specs. */ + includeFilePattern: string | undefined; + includeDirectoryPattern: string | undefined; + excludePattern: string | undefined; + basePaths: readonly string[]; +} + +/** @internal */ +export interface FileSystemEntries { + readonly files: readonly string[]; + readonly directories: readonly string[]; +} + +/** + * @param path directory of the tsconfig.json + * + * @internal + */ +export function getFileMatcherPatterns(path: string, excludes: readonly string[] | undefined, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns { + path = normalizePath(path); + currentDirectory = normalizePath(currentDirectory); + const absolutePath = combinePaths(currentDirectory, path); + + return { + includeFilePatterns: map(getRegularExpressionsForWildcards(includes, absolutePath, "files"), pattern => `^${pattern}$`), + includeFilePattern: getRegularExpressionForWildcard(includes, absolutePath, "files"), + includeDirectoryPattern: getRegularExpressionForWildcard(includes, absolutePath, "directories"), + excludePattern: getRegularExpressionForWildcard(excludes, absolutePath, "exclude"), + basePaths: getBasePaths(path, includes, useCaseSensitiveFileNames) + }; +} + +/** + * @param path directory of the tsconfig.json + * + * @internal + */ +export function matchFiles(path: string, extensions: readonly string[] | undefined, excludes: readonly string[] | undefined, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries, realpath: (path: string) => string): string[] { + path = normalizePath(path); + currentDirectory = normalizePath(currentDirectory); + + const patterns = getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames, currentDirectory); + + const includeFileRegexes = patterns.includeFilePatterns && patterns.includeFilePatterns.map(pattern => getRegexFromPattern(pattern, useCaseSensitiveFileNames)); + const includeDirectoryRegex = patterns.includeDirectoryPattern && getRegexFromPattern(patterns.includeDirectoryPattern, useCaseSensitiveFileNames); + const excludeRegex = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, useCaseSensitiveFileNames); + + // Associate an array of results with each include regex. This keeps results in order of the "include" order. + // If there are no "includes", then just put everything in results[0]. + const results: string[][] = includeFileRegexes ? includeFileRegexes.map(() => []) : [[]]; + const visited = new Map(); + const toCanonical = createGetCanonicalFileName(useCaseSensitiveFileNames); + for (const basePath of patterns.basePaths) { + visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth); + } + + return flatten(results); + + function visitDirectory(path: string, absolutePath: string, depth: number | undefined) { + const canonicalPath = toCanonical(realpath(absolutePath)); + if (visited.has(canonicalPath)) return; + visited.set(canonicalPath, true); + const { files, directories } = getFileSystemEntries(path); + + for (const current of sort(files, compareStringsCaseSensitive)) { + const name = combinePaths(path, current); + const absoluteName = combinePaths(absolutePath, current); + if (extensions && !fileExtensionIsOneOf(name, extensions)) continue; + if (excludeRegex && excludeRegex.test(absoluteName)) continue; + if (!includeFileRegexes) { + results[0].push(name); + } + else { + const includeIndex = findIndex(includeFileRegexes, re => re.test(absoluteName)); + if (includeIndex !== -1) { + results[includeIndex].push(name); + } + } + } + + if (depth !== undefined) { + depth--; + if (depth === 0) { + return; + } + } + + for (const current of sort(directories, compareStringsCaseSensitive)) { + const name = combinePaths(path, current); + const absoluteName = combinePaths(absolutePath, current); + if ((!includeDirectoryRegex || includeDirectoryRegex.test(absoluteName)) && + (!excludeRegex || !excludeRegex.test(absoluteName))) { + visitDirectory(name, absoluteName, depth); + } + } + } +} + +/** @internal */ +export function getRegularExpressionForWildcard(specs: readonly string[] | undefined, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined { + const patterns = getRegularExpressionsForWildcards(specs, basePath, usage); + if (!patterns || !patterns.length) { + return undefined; + } + + const pattern = patterns.map(pattern => `(${pattern})`).join("|"); + // If excluding, match "foo/bar/baz...", but if including, only allow "foo". + const terminator = usage === "exclude" ? "($|/)" : "$"; + return `^(${pattern})${terminator}`; +} + +/** @internal */ +export function getRegularExpressionsForWildcards(specs: readonly string[] | undefined, basePath: string, usage: "files" | "directories" | "exclude"): readonly string[] | undefined { + if (specs === undefined || specs.length === 0) { + return undefined; + } + + return flatMap(specs, spec => + spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage])); +} + +/** @internal */ +export function getRegexFromPattern(pattern: string, useCaseSensitiveFileNames: boolean): RegExp { + return new RegExp(pattern, useCaseSensitiveFileNames ? "" : "i"); +} + +/** @internal */ +export function getPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude") { + const pattern = spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]); + return pattern && `^(${pattern})${usage === "exclude" ? "($|/)" : "$"}`; +} + +/** + * An "includes" path "foo" is implicitly a glob "foo/** /*" (without the space) if its last component has no extension, + * and does not contain any glob characters itself. + * + * @internal + */ +export function isImplicitGlob(lastPathComponent: string): boolean { + return !/[.*?]/.test(lastPathComponent); +} + +function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined { + let subpattern = ""; + let hasWrittenComponent = false; + const components = getNormalizedPathComponents(spec, basePath); + const lastComponent = last(components); + if (usage !== "exclude" && lastComponent === "**") { + return undefined; + } + + // getNormalizedPathComponents includes the separator for the root component. + // We need to remove to create our regex correctly. + components[0] = removeTrailingDirectorySeparator(components[0]); + + if (isImplicitGlob(lastComponent)) { + components.push("**", "*"); + } + + let optionalCount = 0; + for (let component of components) { + if (component === "**") { + subpattern += doubleAsteriskRegexFragment; + } + else { + if (usage === "directories") { + subpattern += "("; + optionalCount++; + } + + if (hasWrittenComponent) { + subpattern += directorySeparator; + } + + if (usage !== "exclude") { + let componentPattern = ""; + // The * and ? wildcards should not match directories or files that start with . if they + // appear first in a component. Dotted directories and files can be included explicitly + // like so: **/.*/.* + if (component.charCodeAt(0) === CharacterCodes.asterisk) { + componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?"; + component = component.substr(1); + } + else if (component.charCodeAt(0) === CharacterCodes.question) { + componentPattern += "[^./]"; + component = component.substr(1); + } + + componentPattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); + + // Patterns should not include subfolders like node_modules unless they are + // explicitly included as part of the path. + // + // As an optimization, if the component pattern is the same as the component, + // then there definitely were no wildcard characters and we do not need to + // add the exclusion pattern. + if (componentPattern !== component) { + subpattern += implicitExcludePathRegexPattern; + } + + subpattern += componentPattern; + } + else { + subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); + } + } + + hasWrittenComponent = true; + } + + while (optionalCount > 0) { + subpattern += ")?"; + optionalCount--; + } + + return subpattern; +} + + + +/** @internal */ +export function regExpEscape(text: string) { + return text.replace(reservedCharacterPattern, escapeRegExpCharacter); +} + +function escapeRegExpCharacter(match: string) { + return "\\" + match; +} + +/** + * Computes the unique non-wildcard base paths amongst the provided include patterns. + */ +function getBasePaths(path: string, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean): string[] { + // Storage for our results in the form of literal paths (e.g. the paths as written by the user). + const basePaths: string[] = [path]; + + if (includes) { + // Storage for literal base paths amongst the include patterns. + const includeBasePaths: string[] = []; + for (const include of includes) { + // We also need to check the relative paths by converting them to absolute and normalizing + // in case they escape the base path (e.g "..\somedirectory") + const absolute: string = isRootedDiskPath(include) ? include : normalizePath(combinePaths(path, include)); + // Append the literal and canonical candidate base paths. + includeBasePaths.push(getIncludeBasePath(absolute)); + } + + // Sort the offsets array using either the literal or canonical path representations. + includeBasePaths.sort(getStringComparer(!useCaseSensitiveFileNames)); + + // Iterate over each include base path and include unique base paths that are not a + // subpath of an existing base path + for (const includeBasePath of includeBasePaths) { + if (every(basePaths, basePath => !containsPath(basePath, includeBasePath, path, !useCaseSensitiveFileNames))) { + basePaths.push(includeBasePath); + } + } + } + + return basePaths; +} + +function getIncludeBasePath(absolute: string): string { + const wildcardOffset = indexOfAnyCharCode(absolute, wildcardCharCodes); + if (wildcardOffset < 0) { + // No "*" or "?" in the path + return !hasExtension(absolute) + ? absolute + : removeTrailingDirectorySeparator(getDirectoryPath(absolute)); + } + return absolute.substring(0, absolute.lastIndexOf(directorySeparator, wildcardOffset)); +} + +function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: string) { + return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match; +} diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index c860198172c30..838f2c2b6103e 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -1,113 +1,129 @@ +import { + DiagnosticReporter, + moduleResolutionOptionDeclarations, +} from "./commandLineParser"; +import { readJson } from "./commandLineParserUtilities"; import { append, appendIfUnique, arrayFrom, arrayIsEqualTo, - changeAnyExtension, - CharacterCodes, - combinePaths, - CommandLineOption, - comparePaths, - Comparison, - CompilerOptions, concatenate, contains, - containsPath, - createCompilerDiagnostic, - Debug, deduplicate, - Diagnostic, - DiagnosticMessage, - DiagnosticReporter, - Diagnostics, - directoryProbablyExists, - directorySeparator, emptyArray, endsWith, - ensureTrailingDirectorySeparator, every, - Extension, - extensionIsTS, - fileExtensionIs, - fileExtensionIsOneOf, filter, firstDefined, forEach, + GetCanonicalFileName, + getOwnKeys, + hasProperty, + isArray, + isString, + lastOrUndefined, + length, + matchedText, + noop, + Pattern, + patternText, + removePrefix, + some, + sort, + startsWith, + stringContains, +} from "./core"; +import { + Comparison, + MapLike, + version, + versionMajorMinor, +} from "./corePublic"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { getCommonSourceDirectory } from "./emitter"; +import { + extensionIsTS, + getPossibleOriginalInputExtensionForExtension, + removeExtension, + removeFileExtension, + supportedDeclarationExtensions, + supportedTSImplementationExtensions, + tryExtractTSExtension, + tryGetExtensionFromPath, +} from "./extension"; +import { isDeclarationFileName } from "./parser"; +import { + changeAnyExtension, + combinePaths, + comparePaths, + containsPath, + directorySeparator, + ensureTrailingDirectorySeparator, + fileExtensionIs, + fileExtensionIsOneOf, forEachAncestorDirectory, - formatMessage, getBaseFileName, - GetCanonicalFileName, - getCommonSourceDirectory, - getCompilerOptionValue, getDirectoryPath, - GetEffectiveTypeRootsHost, - getEmitModuleKind, - getEmitModuleResolutionKind, getNormalizedAbsolutePath, - getOwnKeys, getPathComponents, getPathFromPathComponents, - getPathsBasePath, - getPossibleOriginalInputExtensionForExtension, getRelativePathFromDirectory, - getResolveJsonModule, getRootLength, - hasProperty, hasTrailingDirectorySeparator, - hostGetCanonicalFileName, - inferredTypesContainingFile, - isArray, - isDeclarationFileName, - isExternalModuleNameRelative, isRootedDiskPath, - isString, - lastOrUndefined, - length, - MapLike, - matchedText, + normalizePath, + normalizeSlashes, + pathIsRelative, + toPath, +} from "./path"; +import { perfLogger } from "./perfLogger"; +import { inferredTypesContainingFile } from "./program"; +import { + Version, + VersionRange, +} from "./semver"; +import { + CharacterCodes, + CommandLineOption, + CompilerOptions, + Diagnostic, + DiagnosticMessage, + Extension, + GetEffectiveTypeRootsHost, MatchingKeys, - matchPatternOrExact, ModuleKind, ModuleResolutionHost, ModuleResolutionKind, - moduleResolutionOptionDeclarations, - moduleResolutionSupportsPackageJsonExportsAndImports, - noop, - normalizePath, - normalizeSlashes, PackageId, - packageIdToString, Path, - pathIsRelative, - Pattern, - patternText, - perfLogger, - readJson, - removeExtension, - removeFileExtension, - removePrefix, ResolutionMode, ResolutionNameAndModeGetter, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirective, ResolvedTypeReferenceDirectiveWithFailedLookupLocations, - some, - sort, SourceFile, - startsWith, - stringContains, - supportedDeclarationExtensions, - supportedTSImplementationExtensions, - toPath, - tryExtractTSExtension, - tryGetExtensionFromPath, +} from "./types"; +import { + createCompilerDiagnostic, + directoryProbablyExists, + formatMessage, + getCompilerOptionValue, + getEmitModuleKind, + getEmitModuleResolutionKind, + getPathsBasePath, + getResolveJsonModule, + hostGetCanonicalFileName, + matchPatternOrExact, + moduleResolutionSupportsPackageJsonExportsAndImports, + packageIdToString, tryParsePatterns, - Version, - version, - versionMajorMinor, - VersionRange, -} from "./_namespaces/ts"; +} from "./utilities"; +import { + isExternalModuleNameRelative, +} from "./utilitiesPublic"; /** @internal */ export function trace(host: ModuleResolutionHost, message: DiagnosticMessage, ...args: any[]): void; diff --git a/src/compiler/moduleNameResolverUtilities.ts b/src/compiler/moduleNameResolverUtilities.ts new file mode 100644 index 0000000000000..3ee6e612784c1 --- /dev/null +++ b/src/compiler/moduleNameResolverUtilities.ts @@ -0,0 +1,66 @@ +import { removeFileExtension } from "./extension"; +import { + ensurePathIsNonModuleName, + ensureTrailingDirectorySeparator, + getDirectoryPath, + getNormalizedAbsolutePath, + getRelativePathToDirectoryOrUrl, + pathIsRelative, + toPath, +} from "./path"; +import { + EmitResolver, + ExportDeclaration, + ImportDeclaration, + ImportEqualsDeclaration, + ImportTypeNode, + ModuleDeclaration, + SourceFile, +} from "./types"; +import { getExternalModuleName } from "./utilities"; +import { isStringLiteralLike } from "./utilitiesPublic"; + +/** @internal */ +export interface ResolveModuleNameResolutionHost { + getCanonicalFileName(p: string): string; + getCommonSourceDirectory(): string; + getCurrentDirectory(): string; +} + +/** @internal */ +export function getResolvedExternalModuleName(host: ResolveModuleNameResolutionHost, file: SourceFile, referenceFile?: SourceFile): string { + return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName); +} + +function getCanonicalAbsolutePath(host: ResolveModuleNameResolutionHost, path: string) { + return host.getCanonicalFileName(getNormalizedAbsolutePath(path, host.getCurrentDirectory())); +} + +/** @internal */ +export function getExternalModuleNameFromDeclaration(host: ResolveModuleNameResolutionHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string | undefined { + const file = resolver.getExternalModuleFileFromDeclaration(declaration); + if (!file || file.isDeclarationFile) { + return undefined; + } + // If the declaration already uses a non-relative name, and is outside the common source directory, continue to use it + const specifier = getExternalModuleName(declaration); + if (specifier && isStringLiteralLike(specifier) && !pathIsRelative(specifier.text) && + getCanonicalAbsolutePath(host, file.path).indexOf(getCanonicalAbsolutePath(host, ensureTrailingDirectorySeparator(host.getCommonSourceDirectory()))) === -1) { + return undefined; + } + return getResolvedExternalModuleName(host, file); +} + +/** + * Resolves a local path to a path which is absolute to the base of the emit + * + * @internal + */ +export function getExternalModuleNameFromPath(host: ResolveModuleNameResolutionHost, fileName: string, referencePath?: string): string { + const getCanonicalFileName = (f: string) => host.getCanonicalFileName(f); + const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName); + const filePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory()); + const relativePath = getRelativePathToDirectoryOrUrl(dir, filePath, dir, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false); + const extensionless = removeFileExtension(relativePath); + return referencePath ? ensurePathIsNonModuleName(extensionless) : extensionless; +} diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index ae2025148fd4b..283aa515780d7 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -1,114 +1,132 @@ import { - __String, - allKeysStartWithDot, - AmbientModuleDeclaration, append, arrayFrom, - CharacterCodes, - combinePaths, compareBooleans, - compareNumberOfDirectorySeparators, - comparePaths, - Comparison, - CompilerOptions, - containsIgnoredPath, - containsPath, createGetCanonicalFileName, - Debug, - directorySeparator, emptyArray, endsWith, - ensurePathIsNonModuleName, - ensureTrailingDirectorySeparator, every, - ExportAssignment, - Extension, - extensionFromPath, - fileExtensionIsOneOf, - FileIncludeKind, firstDefined, flatMap, flatten, forEach, - forEachAncestorDirectory, - getBaseFileName, GetCanonicalFileName, - getConditions, - getDirectoryPath, - getEmitModuleResolutionKind, - getModeForResolutionAtIndex, - getModuleNameStringLiteralAt, - getModuleSpecifierEndingPreference, - getNodeModulePathParts, - getNormalizedAbsolutePath, getOwnKeys, - getPackageJsonTypesVersionsPaths, - getPackageNameFromTypesPackageName, - getPathsBasePath, - getRelativePathFromDirectory, - getRelativePathToDirectoryOrUrl, - getResolvePackageJsonExports, - getSourceFileOfModule, + isString, + map, + mapDefined, + min, + removeSuffix, + some, + startsWith, + stringContains, +} from "./core"; +import { + Comparison, + MapLike, +} from "./corePublic"; +import * as Debug from "./debug"; +import { + extensionFromPath, getSupportedExtensions, - getTextOfIdentifierOrLiteral, hasJSFileExtension, hasTSFileExtension, - hostGetCanonicalFileName, - Identifier, - isAmbientModule, - isApplicableVersionedTypesKey, - isDeclarationFileName, - isExternalModuleAugmentation, - isExternalModuleNameRelative, + removeExtension, + removeFileExtension, + tryGetExtensionFromPath, +} from "./extension"; +import { isModuleBlock, isModuleDeclaration, - isNonGlobalAmbientModule, - isRootedDiskPath, isSourceFile, - isString, +} from "./factory/nodeTests"; +import { + allKeysStartWithDot, + getConditions, + getPackageJsonTypesVersionsPaths, + getPackageNameFromTypesPackageName, + isApplicableVersionedTypesKey, + pathContainsNodeModules, + shouldAllowImportingTsExtension, +} from "./moduleNameResolver"; +import { + getModuleSpecifierEndingPreference, + getNodeModulePathParts, + NodeModulePathParts, +} from "./moduleSpecifiersUtilities"; +import { isDeclarationFileName } from "./parser"; +import { + combinePaths, + comparePaths, + containsPath, + directorySeparator, + ensurePathIsNonModuleName, + ensureTrailingDirectorySeparator, + fileExtensionIsOneOf, + forEachAncestorDirectory, + getBaseFileName, + getDirectoryPath, + getNormalizedAbsolutePath, + getRelativePathFromDirectory, + getRelativePathToDirectoryOrUrl, + isRootedDiskPath, + normalizePath, + pathIsBareSpecifier, + pathIsRelative, + resolvePath, + startsWithDirectory, + toPath, +} from "./path"; +import { + getModeForResolutionAtIndex, + getModuleNameStringLiteralAt, +} from "./program"; +import { containsIgnoredPath } from "./sysUtilities"; +import { + __String, + AmbientModuleDeclaration, + CharacterCodes, + CompilerOptions, + ExportAssignment, + Extension, + FileIncludeKind, + Identifier, JsxEmit, - map, - mapDefined, - MapLike, - matchPatternOrExact, - min, ModuleDeclaration, ModuleKind, ModulePath, ModuleResolutionKind, ModuleSpecifierCache, - ModuleSpecifierEnding, ModuleSpecifierOptions, ModuleSpecifierResolutionHost, NodeFlags, - NodeModulePathParts, - normalizePath, Path, - pathContainsNodeModules, - pathIsBareSpecifier, - pathIsRelative, PropertyAccessExpression, - removeExtension, - removeFileExtension, - removeSuffix, ResolutionMode, - resolvePath, ScriptKind, - shouldAllowImportingTsExtension, - some, SourceFile, - startsWith, - startsWithDirectory, - stringContains, StringLiteral, Symbol, SymbolFlags, - toPath, - tryGetExtensionFromPath, - tryParsePatterns, TypeChecker, UserPreferences, -} from "./_namespaces/ts"; +} from "./types"; +import { + compareNumberOfDirectorySeparators, + getEmitModuleResolutionKind, + getPathsBasePath, + getResolvePackageJsonExports, + getSourceFileOfModule, + getTextOfIdentifierOrLiteral, + hostGetCanonicalFileName, + isAmbientModule, + isExternalModuleAugmentation, + isNonGlobalAmbientModule, + matchPatternOrExact, + ModuleSpecifierEnding, + tryParsePatterns, +} from "./utilities"; +import { isExternalModuleNameRelative } from "./utilitiesPublic"; // Used by importFixes, getEditsForFileRename, and declaration emit to synthesize import module specifiers. diff --git a/src/compiler/moduleSpecifiersUtilities.ts b/src/compiler/moduleSpecifiersUtilities.ts new file mode 100644 index 0000000000000..d88dc45581173 --- /dev/null +++ b/src/compiler/moduleSpecifiersUtilities.ts @@ -0,0 +1,179 @@ +import { + append, + concatenate, + emptyArray, + firstDefined, + or, +} from "./core"; +import { + extensionsNotSupportingExtensionlessResolution, + hasJSFileExtension, + hasTSFileExtension, +} from "./extension"; +import { isExpressionStatement } from "./factory/nodeTests"; +import { + nodeModulesPathPart, + shouldAllowImportingTsExtension, +} from "./moduleNameResolver"; +import { fileExtensionIsOneOf, pathIsRelative } from "./path"; +import { + CompilerOptions, + ModuleKind, + RequireOrImportCall, + ResolutionMode, + SourceFile, + UserPreferences, +} from "./types"; +import { + isRequireCall, + isRequireVariableStatement, + isSourceFileJS, + ModuleSpecifierEnding, +} from "./utilities"; + +/** @internal */ +export interface NodeModulePathParts { + readonly topLevelNodeModulesIndex: number; + readonly topLevelPackageNameIndex: number; + readonly packageRootIndex: number; + readonly fileNameIndex: number; +} + +/** @internal */ +export function getNodeModulePathParts(fullPath: string): NodeModulePathParts | undefined { + // If fullPath can't be valid module file within node_modules, returns undefined. + // Example of expected pattern: /base/path/node_modules/[@scope/otherpackage/@otherscope/node_modules/]package/[subdirectory/]file.js + // Returns indices: ^ ^ ^ ^ + + let topLevelNodeModulesIndex = 0; + let topLevelPackageNameIndex = 0; + let packageRootIndex = 0; + let fileNameIndex = 0; + + const enum States { + BeforeNodeModules, + NodeModules, + Scope, + PackageContent + } + + let partStart = 0; + let partEnd = 0; + let state = States.BeforeNodeModules; + + while (partEnd >= 0) { + partStart = partEnd; + partEnd = fullPath.indexOf("/", partStart + 1); + switch (state) { + case States.BeforeNodeModules: + if (fullPath.indexOf(nodeModulesPathPart, partStart) === partStart) { + topLevelNodeModulesIndex = partStart; + topLevelPackageNameIndex = partEnd; + state = States.NodeModules; + } + break; + case States.NodeModules: + case States.Scope: + if (state === States.NodeModules && fullPath.charAt(partStart + 1) === "@") { + state = States.Scope; + } + else { + packageRootIndex = partEnd; + state = States.PackageContent; + } + break; + case States.PackageContent: + if (fullPath.indexOf(nodeModulesPathPart, partStart) === partStart) { + state = States.NodeModules; + } + else { + state = States.PackageContent; + } + break; + } + } + + fileNameIndex = partStart; + + return state > States.NodeModules ? { topLevelNodeModulesIndex, topLevelPackageNameIndex, packageRootIndex, fileNameIndex } : undefined; +} + +/** @internal */ +export function getModuleSpecifierEndingPreference(preference: UserPreferences["importModuleSpecifierEnding"], resolutionMode: ResolutionMode, compilerOptions: CompilerOptions, sourceFile: SourceFile): ModuleSpecifierEnding { + if (preference === "js" || resolutionMode === ModuleKind.ESNext) { + // Extensions are explicitly requested or required. Now choose between .js and .ts. + if (!shouldAllowImportingTsExtension(compilerOptions)) { + return ModuleSpecifierEnding.JsExtension; + } + // `allowImportingTsExtensions` is a strong signal, so use .ts unless the file + // already uses .js extensions and no .ts extensions. + return inferPreference() !== ModuleSpecifierEnding.JsExtension + ? ModuleSpecifierEnding.TsExtension + : ModuleSpecifierEnding.JsExtension; + } + if (preference === "minimal") { + return ModuleSpecifierEnding.Minimal; + } + if (preference === "index") { + return ModuleSpecifierEnding.Index; + } + + // No preference was specified. + // Look at imports and/or requires to guess whether .js, .ts, or extensionless imports are preferred. + // N.B. that `Index` detection is not supported since it would require file system probing to do + // accurately, and more importantly, literally nobody wants `Index` and its existence is a mystery. + if (!shouldAllowImportingTsExtension(compilerOptions)) { + // If .ts imports are not valid, we only need to see one .js import to go with that. + return usesExtensionsOnImports(sourceFile) ? ModuleSpecifierEnding.JsExtension : ModuleSpecifierEnding.Minimal; + } + + return inferPreference(); + + function inferPreference() { + let usesJsExtensions = false; + const specifiers = sourceFile.imports.length ? sourceFile.imports.map(i => i.text) : + isSourceFileJS(sourceFile) ? getRequiresAtTopOfFile(sourceFile).map(r => r.arguments[0].text) : + emptyArray; + for (const specifier of specifiers) { + if (pathIsRelative(specifier)) { + if (fileExtensionIsOneOf(specifier, extensionsNotSupportingExtensionlessResolution)) { + // These extensions are not optional, so do not indicate a preference. + continue; + } + if (hasTSFileExtension(specifier)) { + return ModuleSpecifierEnding.TsExtension; + } + if (hasJSFileExtension(specifier)) { + usesJsExtensions = true; + } + } + } + return usesJsExtensions ? ModuleSpecifierEnding.JsExtension : ModuleSpecifierEnding.Minimal; + } +} + +function getRequiresAtTopOfFile(sourceFile: SourceFile): readonly RequireOrImportCall[] { + let nonRequireStatementCount = 0; + let requires: RequireOrImportCall[] | undefined; + for (const statement of sourceFile.statements) { + if (nonRequireStatementCount > 3) { + break; + } + if (isRequireVariableStatement(statement)) { + requires = concatenate(requires, statement.declarationList.declarations.map(d => d.initializer)); + } + else if (isExpressionStatement(statement) && isRequireCall(statement.expression, /*requireStringLiteralLikeArgument*/ true)) { + requires = append(requires, statement.expression); + } + else { + nonRequireStatementCount++; + } + } + return requires || emptyArray; +} + +function usesExtensionsOnImports({ imports }: SourceFile, hasExtension: (text: string) => boolean = or(hasJSFileExtension, hasTSFileExtension)): boolean { + return firstDefined(imports, ({ text }) => pathIsRelative(text) && !fileExtensionIsOneOf(text, extensionsNotSupportingExtensionlessResolution) + ? hasExtension(text) + : undefined) || false; +} diff --git a/src/compiler/objectAllocator.ts b/src/compiler/objectAllocator.ts new file mode 100644 index 0000000000000..19ec7862bd876 --- /dev/null +++ b/src/compiler/objectAllocator.ts @@ -0,0 +1,140 @@ +import { forEach } from "./core"; +import { isDebugging } from "./debug"; +import { tracing } from "./tracing"; +import { + __String, + Identifier, + ModifierFlags, + Mutable, + Node, + NodeFlags, + PrivateIdentifier, + Signature, + SignatureFlags, + SourceFile, + SourceMapSource, + Symbol, + SymbolFlags, + SyntaxKind, + Token, + TransformFlags, + Type, + TypeChecker, + TypeFlags, +} from "./types"; + +/** @internal */ +export interface ObjectAllocator { + getNodeConstructor(): new (kind: SyntaxKind, pos: number, end: number) => Node; + getTokenConstructor(): new (kind: TKind, pos: number, end: number) => Token; + getIdentifierConstructor(): new (kind: SyntaxKind.Identifier, pos: number, end: number) => Identifier; + getPrivateIdentifierConstructor(): new (kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) => PrivateIdentifier; + getSourceFileConstructor(): new (kind: SyntaxKind.SourceFile, pos: number, end: number) => SourceFile; + getSymbolConstructor(): new (flags: SymbolFlags, name: __String) => Symbol; + getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type; + getSignatureConstructor(): new (checker: TypeChecker, flags: SignatureFlags) => Signature; + getSourceMapSourceConstructor(): new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource; +} + +/** @internal */ +export const objectAllocator: ObjectAllocator = { + getNodeConstructor: () => Node as any, + getTokenConstructor: () => Token as any, + getIdentifierConstructor: () => Identifier as any, + getPrivateIdentifierConstructor: () => Node as any, + getSourceFileConstructor: () => Node as any, + getSymbolConstructor: () => Symbol as any, + getTypeConstructor: () => Type as any, + getSignatureConstructor: () => Signature as any, + getSourceMapSourceConstructor: () => SourceMapSource as any, +}; + +function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { + this.flags = flags; + this.escapedName = name; + this.declarations = undefined; + this.valueDeclaration = undefined; + this.id = 0; + this.mergeId = 0; + this.parent = undefined; + this.members = undefined; + this.exports = undefined; + this.exportSymbol = undefined; + this.constEnumOnlyModule = undefined; + this.isReferenced = undefined; + this.isAssigned = undefined; + (this as any).links = undefined; // used by TransientSymbol +} + +function Type(this: Type, checker: TypeChecker, flags: TypeFlags) { + this.flags = flags; + if (isDebugging || tracing) { + this.checker = checker; + } +} + +function Signature(this: Signature, checker: TypeChecker, flags: SignatureFlags) { + this.flags = flags; + if (isDebugging) { + this.checker = checker; + } +} + +function Node(this: Mutable, kind: SyntaxKind, pos: number, end: number) { + this.pos = pos; + this.end = end; + this.kind = kind; + this.id = 0; + this.flags = NodeFlags.None; + this.modifierFlagsCache = ModifierFlags.None; + this.transformFlags = TransformFlags.None; + this.parent = undefined!; + this.original = undefined; + this.emitNode = undefined; +} + +function Token(this: Mutable, kind: SyntaxKind, pos: number, end: number) { + this.pos = pos; + this.end = end; + this.kind = kind; + this.id = 0; + this.flags = NodeFlags.None; + this.transformFlags = TransformFlags.None; + this.parent = undefined!; + this.emitNode = undefined; +} + +function Identifier(this: Mutable, kind: SyntaxKind, pos: number, end: number) { + this.pos = pos; + this.end = end; + this.kind = kind; + this.id = 0; + this.flags = NodeFlags.None; + this.transformFlags = TransformFlags.None; + this.parent = undefined!; + this.original = undefined; + this.emitNode = undefined; +} + +function SourceMapSource(this: SourceMapSource, fileName: string, text: string, skipTrivia?: (pos: number) => number) { + this.fileName = fileName; + this.text = text; + this.skipTrivia = skipTrivia || (pos => pos); +} + +const objectAllocatorPatchers: ((objectAllocator: ObjectAllocator) => void)[] = []; + +/** + * Used by `deprecatedCompat` to patch the object allocator to apply deprecations. + * @internal + */ +export function addObjectAllocatorPatcher(fn: (objectAllocator: ObjectAllocator) => void) { + objectAllocatorPatchers.push(fn); + fn(objectAllocator); +} + +/** @internal */ +export function setObjectAllocator(alloc: ObjectAllocator) { + Object.assign(objectAllocator, alloc); + forEach(objectAllocatorPatchers, fn => fn(objectAllocator)); +} diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index fb31e3dbdc518..099e23856cec0 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1,8 +1,91 @@ +import { convertToJson } from "./commandLineParser"; import { - AccessorDeclaration, addRange, - addRelatedInfo, append, + concatenate, + emptyArray, + emptyMap, + findIndex, + firstOrUndefined, + forEach, + getSpellingSuggestion, + isArray, + lastOrUndefined, + map, + mapDefined, + noop, + some, + startsWith, + stringContains, + toArray, + trimString, + trimStringEnd, +} from "./core"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { supportedDeclarationExtensions } from "./extension"; +import { BaseNodeFactory } from "./factory/baseNodeFactory"; +import { + createNodeFactory, + NodeFactoryFlags, +} from "./factory/nodeFactory"; +import { + isAsyncModifier, + isExportAssignment, + isExportDeclaration, + isExportModifier, + isExpressionWithTypeArguments, + isExternalModuleReference, + isFunctionTypeNode, + isIdentifier as isIdentifierNode, + isImportDeclaration, + isImportEqualsDeclaration, + isJSDocFunctionType, + isJSDocNullableType, + isJSDocReturnTag, + isJSDocTypeTag, + isJsxOpeningElement, + isJsxOpeningFragment, + isMetaProperty, + isNonNullExpression, + isPrivateIdentifier, + isSetAccessorDeclaration, + isTaggedTemplateExpression, + isTypeReferenceNode, +} from "./factory/nodeTests"; +import { + canHaveModifiers, + setTextRange, +} from "./factory/utilitiesPublic"; +import { PackageJsonInfo } from "./moduleNameResolver"; +import { objectAllocator } from "./objectAllocator"; +import { + containsParseError, + getLastChild, + setParent, + setParentRecursive, +} from "./parserUtilities"; +import { + fileExtensionIs, + fileExtensionIsOneOf, + getBaseFileName, + normalizePath, +} from "./path"; +import { perfLogger } from "./perfLogger"; +import * as performance from "./performance"; +import { + createScanner, + getLeadingCommentRanges, + isIdentifierText, + skipTrivia, + tokenIsIdentifierOrKeyword, + tokenIsIdentifierOrKeywordOrGreaterThan, + tokenToString, +} from "./scanner"; +import { textToKeywordObj } from "./scannerKeywords"; +import { tracing } from "./tracing"; +import { + AccessorDeclaration, ArrayBindingElement, ArrayBindingPattern, ArrayLiteralExpression, @@ -11,11 +94,8 @@ import { AsExpression, AssertClause, AssertEntry, - AssertionLevel, AsteriskToken, - attachFileToDiagnostics, AwaitExpression, - BaseNodeFactory, BinaryExpression, BinaryOperatorToken, BindingElement, @@ -27,8 +107,6 @@ import { BreakStatement, CallExpression, CallSignatureDeclaration, - canHaveJSDoc, - canHaveModifiers, CaseBlock, CaseClause, CaseOrDefaultClause, @@ -44,36 +122,23 @@ import { commentPragmas, CommentRange, ComputedPropertyName, - concatenate, ConditionalExpression, ConditionalTypeNode, ConstructorDeclaration, ConstructorTypeNode, ConstructSignatureDeclaration, - containsParseError, ContinueStatement, - convertToJson, - createDetachedDiagnostic, - createNodeFactory, - createScanner, - createTextChangeRange, - createTextSpanFromBounds, - Debug, Decorator, DefaultClause, DeleteExpression, Diagnostic, DiagnosticArguments, DiagnosticMessage, - Diagnostics, DiagnosticWithDetachedLocation, DoStatement, DotDotDotToken, ElementAccessExpression, - emptyArray, - emptyMap, EndOfFileToken, - ensureScriptKind, EntityName, EnumDeclaration, EnumMember, @@ -86,11 +151,6 @@ import { ExpressionWithTypeArguments, Extension, ExternalModuleReference, - fileExtensionIs, - fileExtensionIsOneOf, - findIndex, - firstOrUndefined, - forEach, ForEachChildNodes, ForInOrOfStatement, ForInStatement, @@ -101,21 +161,10 @@ import { FunctionOrConstructorTypeNode, FunctionTypeNode, GetAccessorDeclaration, - getBaseFileName, - getBinaryOperatorPrecedence, - getFullWidth, - getJSDocCommentRanges, - getLanguageVariant, - getLastChild, - getLeadingCommentRanges, - getSpellingSuggestion, - getTextOfNodeFromSourceText, HasJSDoc, - hasJSDocNodes, HasModifiers, HeritageClause, Identifier, - idText, IfStatement, ImportClause, ImportDeclaration, @@ -129,39 +178,6 @@ import { InferTypeNode, InterfaceDeclaration, IntersectionTypeNode, - isArray, - isAssignmentOperator, - isAsyncModifier, - isClassMemberModifier, - isExportAssignment, - isExportDeclaration, - isExportModifier, - isExpressionWithTypeArguments, - isExternalModuleReference, - isFunctionTypeNode, - isIdentifier as isIdentifierNode, - isIdentifierText, - isImportDeclaration, - isImportEqualsDeclaration, - isJSDocFunctionType, - isJSDocNullableType, - isJSDocReturnTag, - isJSDocTypeTag, - isJsxOpeningElement, - isJsxOpeningFragment, - isKeyword, - isKeywordOrPunctuation, - isLeftHandSideExpression, - isLiteralKind, - isMetaProperty, - isModifierKind, - isNonNullExpression, - isPrivateIdentifier, - isSetAccessorDeclaration, - isStringOrNumericLiteralLike, - isTaggedTemplateExpression, - isTemplateLiteralKind, - isTypeReferenceNode, IterationStatement, JSDoc, JSDocAllType, @@ -233,13 +249,10 @@ import { JsxTokenSyntaxKind, LabeledStatement, LanguageVariant, - lastOrUndefined, LeftHandSideExpression, LiteralExpression, LiteralLikeNode, LiteralTypeNode, - map, - mapDefined, MappedTypeNode, MemberExpression, MetaProperty, @@ -250,7 +263,6 @@ import { Modifier, ModifierFlags, ModifierLike, - modifiersToFlags, ModuleBlock, ModuleDeclaration, ModuleKind, @@ -268,28 +280,19 @@ import { Node, NodeArray, NodeFactory, - NodeFactoryFlags, NodeFlags, - nodeIsMissing, - nodeIsPresent, NonNullExpression, - noop, - normalizePath, NoSubstitutionTemplateLiteral, NullLiteral, NumericLiteral, - objectAllocator, ObjectBindingPattern, ObjectLiteralElementLike, ObjectLiteralExpression, - OperatorPrecedence, OptionalTypeNode, - PackageJsonInfo, ParameterDeclaration, ParenthesizedExpression, ParenthesizedTypeNode, PartiallyEmittedExpression, - perfLogger, PlusToken, PostfixUnaryExpression, PostfixUnaryOperator, @@ -324,23 +327,12 @@ import { ScriptKind, ScriptTarget, SetAccessorDeclaration, - setParent, - setParentRecursive, - setTextRange, - setTextRangePos, - setTextRangePosEnd, - setTextRangePosWidth, ShorthandPropertyAssignment, - skipTrivia, - some, SourceFile, SpreadAssignment, SpreadElement, - startsWith, Statement, - stringContains, StringLiteral, - supportedDeclarationExtensions, SwitchStatement, SyntaxKind, TaggedTemplateExpression, @@ -353,24 +345,13 @@ import { TemplateSpan, TemplateTail, TextChangeRange, - textChangeRangeIsUnchanged, - textChangeRangeNewSpan, TextRange, - textSpanEnd, - textToKeywordObj, ThisExpression, ThisTypeNode, ThrowStatement, - toArray, Token, TokenFlags, - tokenIsIdentifierOrKeyword, - tokenIsIdentifierOrKeywordOrGreaterThan, - tokenToString, - tracing, TransformFlags, - trimString, - trimStringEnd, TryStatement, TupleTypeNode, TypeAliasDeclaration, @@ -385,7 +366,6 @@ import { TypeQueryNode, TypeReferenceNode, UnaryExpression, - unescapeLeadingUnderscores, UnionOrIntersectionTypeNode, UnionTypeNode, UpdateExpression, @@ -396,8 +376,46 @@ import { WhileStatement, WithStatement, YieldExpression, -} from "./_namespaces/ts"; -import * as performance from "./_namespaces/ts.performance"; +} from "./types"; +import { + addRelatedInfo, + attachFileToDiagnostics, + canHaveJSDoc, + createDetachedDiagnostic, + ensureScriptKind, + getBinaryOperatorPrecedence, + getFullWidth, + getJSDocCommentRanges, + getLanguageVariant, + getTextOfNodeFromSourceText, + isAssignmentOperator, + isExternalModule, + isKeyword, + isKeywordOrPunctuation, + isStringOrNumericLiteralLike, + modifiersToFlags, + nodeIsMissing, + nodeIsPresent, + OperatorPrecedence, + setTextRangePos, + setTextRangePosEnd, + setTextRangePosWidth, +} from "./utilities"; +import { + createTextChangeRange, + createTextSpanFromBounds, + hasJSDocNodes, + idText, + isClassMemberModifier, + isLeftHandSideExpression, + isLiteralKind, + isModifierKind, + isTemplateLiteralKind, + textChangeRangeIsUnchanged, + textChangeRangeNewSpan, + textSpanEnd, + unescapeLeadingUnderscores, +} from "./utilitiesPublic"; const enum SignatureFlags { None = 0, @@ -1369,11 +1387,6 @@ export function parseJsonText(fileName: string, sourceText: string): JsonSourceF return Parser.parseJsonText(fileName, sourceText); } -// See also `isExternalOrCommonJsModule` in utilities.ts -export function isExternalModule(file: SourceFile): boolean { - return file.externalModuleIndicator !== undefined; -} - // Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter // indicates what changed between the 'text' that this SourceFile has and the 'newText'. // The SourceFile will be created with the compiler attempting to reuse as many nodes from @@ -9587,7 +9600,7 @@ namespace Parser { namespace IncrementalParser { export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile { - aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive); + aggressiveChecks = aggressiveChecks || Debug.shouldAssert(Debug.AssertionLevel.Aggressive); checkChangeRange(sourceFile, newText, textChangeRange, aggressiveChecks); if (textChangeRangeIsUnchanged(textChangeRange)) { @@ -10074,7 +10087,7 @@ namespace IncrementalParser { if (textChangeRange) { Debug.assert((oldText.length - textChangeRange.span.length + textChangeRange.newLength) === newText.length); - if (aggressiveChecks || Debug.shouldAssert(AssertionLevel.VeryAggressive)) { + if (aggressiveChecks || Debug.shouldAssert(Debug.AssertionLevel.VeryAggressive)) { const oldTextPrefix = oldText.substr(0, textChangeRange.span.start); const newTextPrefix = newText.substr(0, textChangeRange.span.start); Debug.assert(oldTextPrefix === newTextPrefix); diff --git a/src/compiler/parserUtilities.ts b/src/compiler/parserUtilities.ts new file mode 100644 index 0000000000000..8ef8f06349c76 --- /dev/null +++ b/src/compiler/parserUtilities.ts @@ -0,0 +1,209 @@ +import { + forEachChild, + forEachChildRecursively, +} from "./parser"; +import { + Block, + Mutable, + Node, + NodeFlags, + ReturnStatement, + Statement, + SyntaxKind, + YieldExpression, +} from "./types"; +import { + isPartOfTypeNode, + nodeIsPresent, +} from "./utilities"; +import { + hasJSDocNodes, + isFunctionLike, + isJSDocNode, +} from "./utilitiesPublic"; + +function aggregateChildData(node: Node): void { + if (!(node.flags & NodeFlags.HasAggregatedChildData)) { + // A node is considered to contain a parse error if: + // a) the parser explicitly marked that it had an error + // b) any of it's children reported that it had an error. + const thisNodeOrAnySubNodesHasError = ((node.flags & NodeFlags.ThisNodeHasError) !== 0) || + forEachChild(node, containsParseError); + + // If so, mark ourselves accordingly. + if (thisNodeOrAnySubNodesHasError) { + (node as Mutable).flags |= NodeFlags.ThisNodeOrAnySubNodesHasError; + } + + // Also mark that we've propagated the child information to this node. This way we can + // always consult the bit directly on this node without needing to check its children + // again. + (node as Mutable).flags |= NodeFlags.HasAggregatedChildData; + } +} + +/** @internal */ +export function containsParseError(node: Node): boolean { + aggregateChildData(node); + return (node.flags & NodeFlags.ThisNodeOrAnySubNodesHasError) !== 0; +} + +/** @internal */ +export function getLastChild(node: Node): Node | undefined { + let lastChild: Node | undefined; + forEachChild(node, + child => { + if (nodeIsPresent(child)) lastChild = child; + }, + children => { + // As an optimization, jump straight to the end of the list. + for (let i = children.length - 1; i >= 0; i--) { + if (nodeIsPresent(children[i])) { + lastChild = children[i]; + break; + } + } + }); + return lastChild; +} + +// Warning: This has the same semantics as the forEach family of functions, +// in that traversal terminates in the event that 'visitor' supplies a truthy value. +/** @internal */ +export function forEachReturnStatement(body: Block | Statement, visitor: (stmt: ReturnStatement) => T): T | undefined { + + return traverse(body); + + function traverse(node: Node): T | undefined { + switch (node.kind) { + case SyntaxKind.ReturnStatement: + return visitor(node as ReturnStatement); + case SyntaxKind.CaseBlock: + case SyntaxKind.Block: + case SyntaxKind.IfStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.SwitchStatement: + case SyntaxKind.CaseClause: + case SyntaxKind.DefaultClause: + case SyntaxKind.LabeledStatement: + case SyntaxKind.TryStatement: + case SyntaxKind.CatchClause: + return forEachChild(node, traverse); + } + } +} + +/** @internal */ +export function forEachYieldExpression(body: Block, visitor: (expr: YieldExpression) => void): void { + return traverse(body); + + function traverse(node: Node): void { + switch (node.kind) { + case SyntaxKind.YieldExpression: + visitor(node as YieldExpression); + const operand = (node as YieldExpression).expression; + if (operand) { + traverse(operand); + } + return; + case SyntaxKind.EnumDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.TypeAliasDeclaration: + // These are not allowed inside a generator now, but eventually they may be allowed + // as local types. Regardless, skip them to avoid the work. + return; + default: + if (isFunctionLike(node)) { + if (node.name && node.name.kind === SyntaxKind.ComputedPropertyName) { + // Note that we will not include methods/accessors of a class because they would require + // first descending into the class. This is by design. + traverse(node.name.expression); + return; + } + } + else if (!isPartOfTypeNode(node)) { + // This is the general case, which should include mostly expressions and statements. + // Also includes NodeArrays. + forEachChild(node, traverse); + } + } + } +} + +/** + * Bypasses immutability and directly sets the `parent` property of each `Node` recursively. + * @param rootNode The root node from which to start the recursion. + * @param incremental When `true`, only recursively descends through nodes whose `parent` pointers are incorrect. + * This allows us to quickly bail out of setting `parent` for subtrees during incremental parsing. + * + * @internal + */ +export function setParentRecursive(rootNode: T, incremental: boolean): T; +/** @internal */ +export function setParentRecursive(rootNode: T | undefined, incremental: boolean): T | undefined; +/** @internal */ +export function setParentRecursive(rootNode: T | undefined, incremental: boolean): T | undefined { + if (!rootNode) return rootNode; + forEachChildRecursively(rootNode, isJSDocNode(rootNode) ? bindParentToChildIgnoringJSDoc : bindParentToChild); + return rootNode; + + function bindParentToChildIgnoringJSDoc(child: Node, parent: Node): void | "skip" { + if (incremental && child.parent === parent) { + return "skip"; + } + setParent(child, parent); + } + + function bindJSDoc(child: Node) { + if (hasJSDocNodes(child)) { + for (const doc of child.jsDoc!) { + bindParentToChildIgnoringJSDoc(doc, child); + forEachChildRecursively(doc, bindParentToChildIgnoringJSDoc); + } + } + } + + function bindParentToChild(child: Node, parent: Node) { + return bindParentToChildIgnoringJSDoc(child, parent) || bindJSDoc(child); + } +} + +/** + * Bypasses immutability and directly sets the `parent` property of a `Node`. + * + * @internal + */ +export function setParent(child: T, parent: T["parent"] | undefined): T; +/** @internal */ +export function setParent(child: T | undefined, parent: T["parent"] | undefined): T | undefined; +/** @internal */ +export function setParent(child: T | undefined, parent: T["parent"] | undefined): T | undefined { + if (child && parent) { + (child as Mutable).parent = parent; + } + return child; +} + +/** + * Bypasses immutability and directly sets the `parent` property of each `Node` in an array of nodes, if is not already set. + * + * @internal + */ +export function setEachParent(children: T, parent: T[number]["parent"]): T; +/** @internal */ +export function setEachParent(children: T | undefined, parent: T[number]["parent"]): T | undefined; +/** @internal */ +export function setEachParent(children: T | undefined, parent: T[number]["parent"]): T | undefined { + if (children) { + for (const child of children) { + setParent(child, parent); + } + } + return children; +} diff --git a/src/compiler/path.ts b/src/compiler/path.ts index 661997d28d354..f39b7da520ac3 100644 --- a/src/compiler/path.ts +++ b/src/compiler/path.ts @@ -1,10 +1,7 @@ import { - CharacterCodes, compareStringsCaseInsensitive, compareStringsCaseSensitive, compareValues, - Comparison, - Debug, endsWith, equateStringsCaseInsensitive, equateStringsCaseSensitive, @@ -12,11 +9,16 @@ import { getStringComparer, identity, lastOrUndefined, - Path, some, startsWith, stringContains, -} from "./_namespaces/ts"; +} from "./core"; +import { Comparison } from "./corePublic"; +import * as Debug from "./debug"; +import { + CharacterCodes, + Path, +} from "./types"; /** * Internally, we represent paths as strings with '/' as the directory separator. diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 4f2096ec7204f..f03040a4e69e8 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -1,13 +1,12 @@ +import { noop } from "./core"; +import * as Debug from "./debug"; import { - Debug, - noop, Performance, PerformanceHooks, - sys, - System, timestamp, tryGetNativePerformanceHooks, -} from "./_namespaces/ts"; +} from "./performanceCore"; +import { System } from "./types"; /** Performance measurements for the compiler. */ @@ -27,7 +26,7 @@ export interface Timer { /** @internal */ export function createTimerIf(condition: boolean, measureName: string, startMarkName: string, endMarkName: string) { - return condition ? createTimer(measureName, startMarkName, endMarkName) : nullTimer; + return condition ? createTimer(measureName, startMarkName, endMarkName) : createNullTimer(); } /** @internal */ @@ -56,7 +55,7 @@ export function createTimer(measureName: string, startMarkName: string, endMarkN } /** @internal */ -export const nullTimer: Timer = { enter: noop, exit: noop }; +export const createNullTimer = (): Timer => ({ enter: noop, exit: noop }); let enabled = false; let timeorigin = timestamp(); @@ -176,7 +175,7 @@ export function isEnabled() { * * @internal */ -export function enable(system: System = sys) { +export function enable(system: System) { if (!enabled) { enabled = true; perfHooks ||= tryGetNativePerformanceHooks(); diff --git a/src/compiler/performanceCore.ts b/src/compiler/performanceCore.ts index 3f8a459bb0d75..9753303b08d5c 100644 --- a/src/compiler/performanceCore.ts +++ b/src/compiler/performanceCore.ts @@ -1,6 +1,4 @@ -import { - isNodeLikeSystem, -} from "./_namespaces/ts"; +import { isNodeLikeSystem } from "./platform"; // The following definitions provide the minimum compatible support for the Web Performance User Timings API // between browsers and NodeJS: diff --git a/src/compiler/platform.ts b/src/compiler/platform.ts new file mode 100644 index 0000000000000..3c40c035c51ab --- /dev/null +++ b/src/compiler/platform.ts @@ -0,0 +1,13 @@ +/** @internal */ +export function isNodeLikeSystem(): boolean { + // This is defined here rather than in sys.ts to prevent a cycle from its + // use in performanceCore.ts. + // + // We don't use the presence of `require` to check if we are in Node; + // when bundled using esbuild, this function will be rewritten to `__require` + // and definitely exist. + return typeof process !== "undefined" + && !!process.nextTick + && !(process as any).browser + && typeof module === "object"; +} diff --git a/src/compiler/program.ts b/src/compiler/program.ts index cb47acffd4b57..53fabc1c803e7 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1,59 +1,212 @@ +import { BuilderProgram } from "./builderPublic"; +import { createTypeChecker } from "./checker"; +import { + DiagnosticReporter, + inverseJsxOptionMap, + libMap, + libs, + ParseConfigFileHost, + parseJsonSourceFileConfigFileContent, + sourceFileAffectingCompilerOptions, + targetOptionDeclaration, +} from "./commandLineParser"; import { - __String, - addInternalEmitFlags, addRange, - addRelatedInfo, append, arrayFrom, arrayIsEqualTo, - AsExpression, - AssertClause, - BuilderProgram, - CancellationToken, - canHaveDecorators, - canHaveIllegalDecorators, - chainDiagnosticMessages, - changeExtension, - changesAffectingProgramStructure, - changesAffectModuleResolution, - combinePaths, - CommentDirective, - CommentDirectivesMap, - compareDataObjects, - comparePaths, compareValues, - Comparison, - CompilerHost, - CompilerOptions, - computeLineAndCharacterOfPosition, concatenate, contains, - containsIgnoredPath, - containsPath, - convertToRelativePath, - createCommentDirectivesMap, - createCompilerDiagnostic, - createCompilerDiagnosticFromMessageChain, - createDiagnosticCollection, - createDiagnosticForNodeFromMessageChain, - createDiagnosticForNodeInSourceFile, - createDiagnosticForRange, - createFileDiagnostic, - createFileDiagnosticFromMessageChain, createGetCanonicalFileName, + createMultiMap, + emptyArray, + equateStringsCaseInsensitive, + equateStringsCaseSensitive, + filter, + find, + findIndex, + firstDefinedIterator, + flatMap, + flatten, + forEach, + GetCanonicalFileName, + getSpellingSuggestion, + hasProperty, + identity, + isArray, + isString, + length, + mapDefined, + mapDefinedIterator, + maybeBind, + memoize, + noop, + or, + padLeft, + removePrefix, + removeSuffix, + returnFalse, + returnUndefined, + some, + stableSort, + startsWith, + stringContains, + toFileNameLowerCase, + trimStringEnd, +} from "./core"; +import { + Comparison, + SortedReadonlyArray, + versionMajorMinor, +} from "./corePublic"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { + emitFiles, + forEachEmittedFile, + getCommonSourceDirectory, + getCommonSourceDirectoryOfConfig, + getOutputDeclarationFileName, + getOutputPathsForBundle, + getTsBuildInfoEmitOutputFilePath, + isBuildInfoFile, + notImplementedResolver, +} from "./emitter"; +import { + changeExtension, + extensionFromPath, + getSupportedExtensions, + getSupportedExtensionsWithJsonIfResolveJsonModule, + hasJSFileExtension, + removeFileExtension, + resolutionExtensionIsTSOrJson, + supportedJSExtensionsFlat, +} from "./extension"; +import { addInternalEmitFlags } from "./factory/emitNode"; +import { createInputFilesWithFilePaths, + factory, +} from "./factory/nodeFactory"; +import { + isArrayLiteralExpression, + isClassDeclaration, + isDecorator, + isDefaultModifier, + isExportDeclaration, + isExportModifier, + isImportDeclaration, + isImportEqualsDeclaration, + isImportSpecifier, + isImportTypeNode, + isJsxFragment, + isModuleDeclaration, + isObjectLiteralExpression, + isParameter, + isStringLiteral, +} from "./factory/nodeTests"; +import { canHaveDecorators } from "./factory/utilitiesPublic"; +import { createModeAwareCache, createModeAwareCacheKey, createModuleResolutionCache, - createMultiMap, - CreateProgramOptions, + createTypeReferenceDirectiveResolutionCache, + getAutomaticTypeDirectiveNames, + getPackageScopeForPath, + getTemporaryModuleResolutionState, + isTraceEnabled, + ModeAwareCache, + ModeAwareCacheKey, + ModuleResolutionCache, + nodeModulesPathPart, + PackageJsonInfoCache, + resolveLibrary, + resolveModuleName, + resolveTypeReferenceDirective, + trace, + TypeReferenceDirectiveResolutionCache, + zipToModeAwareCache, +} from "./moduleNameResolver"; +import { createSourceFile, CreateSourceFileOptions, + forEachChild, + forEachChildRecursively, + isDeclarationFileName, + isFileProbablyExternalModule, + parseIsolatedEntityName, + parseNodeFactory, +} from "./parser"; +import { + setParent, + setParentRecursive, +} from "./parserUtilities"; +import { + combinePaths, + comparePaths, + containsPath, + convertToRelativePath, + directorySeparator, + ensureTrailingDirectorySeparator, + fileExtensionIs, + fileExtensionIsOneOf, + forEachAncestorDirectory, + getBaseFileName, + getDirectoryPath, + getNormalizedAbsolutePath, + getNormalizedAbsolutePathWithoutRoot, + getNormalizedPathComponents, + getPathFromPathComponents, + getRootLength, + hasExtension, + isRootedDiskPath, + normalizePath, + pathIsAbsolute, + pathIsRelative, + toPath, +} from "./path"; +import * as performance from "./performance"; +import { + changesAffectingProgramStructure, + changesAffectModuleResolution, + hasChangesInResolutions, + setResolvedModule, + setResolvedTypeReferenceDirective, +} from "./programUtilities"; +import { + computeLineAndCharacterOfPosition, + getLineAndCharacterOfPosition, + getLineStarts, + getPositionOfLineAndCharacter, + isIdentifierText, + skipTrivia, + tokenToString, +} from "./scanner"; +import { Version } from "./semver"; +import { createSymlinkCache, - createTypeChecker, - createTypeReferenceDirectiveResolutionCache, + SymlinkCache, +} from "./symlinkCache"; +import { sys } from "./sys"; +import { containsIgnoredPath } from "./sysUtilities"; +import { tracing } from "./tracing"; +import { + getTransformers, + noTransformers, +} from "./transformer"; +import { getDeclarationDiagnostics } from "./transformers/declarations"; +import { resolveConfigFileProjectName } from "./tsbuild"; +import { + __String, + AsExpression, + AssertClause, + CancellationToken, + CommentDirective, + CommentDirectivesMap, + CompilerHost, + CompilerOptions, + CreateProgramOptions, CustomTransformers, - Debug, DeclarationWithTypeParameterChildren, Diagnostic, DiagnosticArguments, @@ -61,280 +214,169 @@ import { diagnosticCategoryName, DiagnosticMessage, DiagnosticMessageChain, - DiagnosticReporter, - Diagnostics, DiagnosticWithLocation, - directorySeparator, - DirectoryStructureHost, - emitFiles, EmitHost, - emitModuleKindIsNonNodeESM, EmitOnly, EmitResult, - emptyArray, - ensureTrailingDirectorySeparator, - equateStringsCaseInsensitive, - equateStringsCaseSensitive, - explainIfFileIsRedirectAndImpliedFormat, ExportAssignment, ExportDeclaration, Extension, - extensionFromPath, - externalHelpersModuleNameText, - factory, - fileExtensionIs, - fileExtensionIsOneOf, FileIncludeKind, FileIncludeReason, - fileIncludeReasonToDiagnostics, FilePreprocessingDiagnostics, FilePreprocessingDiagnosticsKind, FileReference, - filter, - find, - findIndex, - firstDefinedIterator, - flatMap, - flatten, - forEach, - forEachAncestorDirectory, - forEachChild, - forEachChildRecursively, - forEachEmittedFile, - forEachEntry, - forEachKey, - forEachPropertyAssignment, - forEachResolvedProjectReference as ts_forEachResolvedProjectReference, - forEachTsConfigPropArray, FunctionLikeDeclaration, - getAllowJSCompilerOption, - getAutomaticTypeDirectiveNames, - getBaseFileName, - GetCanonicalFileName, - getCommonSourceDirectory as ts_getCommonSourceDirectory, - getCommonSourceDirectoryOfConfig, - getDeclarationDiagnostics as ts_getDeclarationDiagnostics, - getDefaultLibFileName, - getDirectoryPath, - getEmitDeclarations, - getEmitModuleKind, - getEmitModuleResolutionKind, - getEmitScriptTarget, - getErrorSpanForNode, - getExternalModuleName, - getIsolatedModules, - getJSXImplicitImportBase, - getJSXRuntimeImport, - getLineAndCharacterOfPosition, - getLineStarts, - getMatchedFileSpec, - getMatchedIncludeSpec, - getNewLineCharacter, - getNormalizedAbsolutePath, - getNormalizedAbsolutePathWithoutRoot, - getNormalizedPathComponents, - getOutputDeclarationFileName, - getOutputPathsForBundle, - getPackageScopeForPath, - getPathFromPathComponents, - getPositionOfLineAndCharacter, - getPropertyArrayElementValue, - getResolvedModule, - getResolveJsonModule, - getRootLength, - getSetExternalModuleIndicator, - getSpellingSuggestion, - getStrictOptionValue, - getSupportedExtensions, - getSupportedExtensionsWithJsonIfResolveJsonModule, - getTemporaryModuleResolutionState, - getTextOfIdentifierOrLiteral, - getTransformers, - getTsBuildInfoEmitOutputFilePath, - getTsConfigObjectLiteralExpression, - getTsConfigPropArrayElementValue, HasChangedAutomaticTypeDirectiveNames, - hasChangesInResolutions, - hasExtension, HasInvalidatedLibResolutions, HasInvalidatedResolutions, - hasJSDocNodes, - hasJSFileExtension, - hasJsonModuleEmitEnabled, - hasProperty, - hasSyntacticModifier, - hasZeroOrOneAsteriskCharacter, HeritageClause, Identifier, - identity, ImportClause, ImportDeclaration, ImportOrExportSpecifier, InputFiles, InternalEmitFlags, - inverseJsxOptionMap, - isAmbientModule, - isAnyImportOrReExport, - isArray, - isArrayLiteralExpression, - isBuildInfoFile, - isCheckJsEnabledForFile, - isClassDeclaration, - isDeclarationFileName, - isDecorator, - isDefaultModifier, - isExportDeclaration, - isExportModifier, - isExternalModule, - isExternalModuleNameRelative, - isIdentifierText, - isImportCall, - isImportDeclaration, - isImportEqualsDeclaration, - isImportSpecifier, - isImportTypeNode, - isIncrementalCompilation, - isInJSFile, - isLiteralImportTypeNode, - isModifier, - isModuleDeclaration, - isObjectLiteralExpression, - isParameter, - isPlainJsFile, - isRequireCall, - isRootedDiskPath, - isSourceFileJS, - isString, - isStringLiteral, - isStringLiteralLike, - isTraceEnabled, JsonSourceFile, JsxEmit, - length, - libMap, LibResolution, - libs, - mapDefined, - mapDefinedIterator, - maybeBind, - memoize, MethodDeclaration, - ModeAwareCache, - ModeAwareCacheKey, ModifierFlags, ModifierLike, ModuleBlock, ModuleDeclaration, + ModuleDetectionKind, ModuleKind, - ModuleResolutionCache, ModuleResolutionHost, - moduleResolutionIsEqualTo, ModuleResolutionKind, - moduleResolutionSupportsPackageJsonExportsAndImports, Mutable, Node, NodeArray, NodeFlags, - nodeModulesPathPart, NodeWithTypeArguments, - noop, - normalizePath, - notImplementedResolver, - noTransformers, ObjectLiteralExpression, OperationCanceledException, - optionsHaveChanges, - outFile, PackageId, - packageIdToPackageName, - packageIdToString, - PackageJsonInfoCache, - padLeft, ParameterDeclaration, - ParseConfigFileHost, ParsedCommandLine, - parseIsolatedEntityName, - parseJsonSourceFileConfigFileContent, - parseNodeFactory, Path, - pathIsAbsolute, - pathIsRelative, Program, - ProgramHost, ProjectReference, ProjectReferenceFile, - projectReferenceIsEqualTo, PropertyAssignment, PropertyDeclaration, ReferencedFile, - removeFileExtension, - removePrefix, - removeSuffix, - resolutionExtensionIsTSOrJson, ResolutionMode, - resolveConfigFileProjectName, + ResolutionNameAndModeGetter, ResolvedConfigFileName, ResolvedModuleFull, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirectiveWithFailedLookupLocations, - resolveLibrary, - resolveModuleName, - resolveTypeReferenceDirective, - returnFalse, - returnUndefined, SatisfiesExpression, ScriptKind, ScriptTarget, - setParent, - setParentRecursive, - setResolvedModule, - setResolvedTypeReferenceDirective, - shouldResolveJsRequire, - skipTrivia, - skipTypeChecking, - some, - sortAndDeduplicateDiagnostics, - SortedReadonlyArray, SourceFile, - sourceFileAffectingCompilerOptions, - sourceFileMayBeEmitted, SourceOfProjectReferenceRedirect, - stableSort, - startsWith, Statement, - stringContains, StringLiteral, StringLiteralLike, StructureIsReused, - supportedJSExtensionsFlat, - SymlinkCache, SyntaxKind, - sys, System, - targetOptionDeclaration, - toFileNameLowerCase, - tokenToString, - toPath as ts_toPath, - trace, - tracing, - trimStringEnd, + TransformFlags, TsConfigSourceFile, TypeChecker, - typeDirectiveIsEqualTo, - TypeReferenceDirectiveResolutionCache, UnparsedSource, VariableDeclaration, VariableStatement, - Version, - versionMajorMinor, - walkUpParenthesizedExpressions, WriteFileCallback, WriteFileCallbackData, +} from "./types"; +import { + addRelatedInfo, + canHaveIllegalDecorators, + chainDiagnosticMessages, + compareDataObjects, + createCommentDirectivesMap, + createCompilerDiagnostic, + createCompilerDiagnosticFromMessageChain, + createDiagnosticCollection, + createDiagnosticForNodeFromMessageChain, + createDiagnosticForNodeInSourceFile, + createDiagnosticForRange, + createFileDiagnostic, + createFileDiagnosticFromMessageChain, + emitModuleKindIsNonNodeESM, + externalHelpersModuleNameText, + forEachEntry, + forEachKey, + forEachPropertyAssignment, + forEachTsConfigPropArray, + getAllowJSCompilerOption, + getEmitDeclarations, + getEmitModuleDetectionKind, + getEmitModuleKind, + getEmitModuleResolutionKind, + getEmitScriptTarget, + getErrorSpanForNode, + getExternalModuleName, + getIsolatedModules, + getJSXImplicitImportBase, + getJSXRuntimeImport, + getNewLineCharacter, + getPropertyArrayElementValue, + getResolvedModule, + getResolveJsonModule, + getStrictOptionValue, + getTextOfIdentifierOrLiteral, + getTsConfigObjectLiteralExpression, + getTsConfigPropArrayElementValue, + hasJsonModuleEmitEnabled, + hasSyntacticModifier, + hasZeroOrOneAsteriskCharacter, + isAmbientModule, + isAnyImportOrReExport, + isCheckJsEnabledForFile, + isExternalModule, + isImportCall, + isIncrementalCompilation, + isInJSFile, + isLiteralImportTypeNode, + isPlainJsFile, + isRequireCall, + isSourceFileJS, + moduleResolutionIsEqualTo, + moduleResolutionSupportsPackageJsonExportsAndImports, + optionsHaveChanges, + outFile, + packageIdToPackageName, + packageIdToString, + projectReferenceIsEqualTo, + shouldResolveJsRequire, + skipTypeChecking, + sourceFileMayBeEmitted, + typeDirectiveIsEqualTo, + walkUpParenthesizedExpressions, writeFileEnsuringDirectories, - zipToModeAwareCache, -} from "./_namespaces/ts"; -import * as performance from "./_namespaces/ts.performance"; +} from "./utilities"; +import { + getDefaultLibFileName, + hasJSDocNodes, + isExternalModuleNameRelative, + isJsxOpeningLikeElement, + isModifier, + isStringLiteralLike, + sortAndDeduplicateDiagnostics, +} from "./utilitiesPublic"; +import { + explainIfFileIsRedirectAndImpliedFormat, + fileIncludeReasonToDiagnostics, + getMatchedFileSpec, + getMatchedIncludeSpec, +} from "./watch"; +import { ProgramHost } from "./watchPublic"; +import { DirectoryStructureHost } from "./watchUtilities"; export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string | undefined { return forEachAncestorDirectory(searchPath, ancestor => { @@ -845,7 +887,7 @@ export interface SourceFileImportsList { * Calculates the resulting resolution mode for some reference in some file - this is generally the explicitly * provided resolution mode in the reference, unless one is not present, in which case it is the mode of the containing file. */ -export function getModeForFileReference(ref: FileReference | string, containingFileMode: ResolutionMode) { +export function getModeForFileReference(ref: FileReference | string, containingFileMode: ResolutionMode): ResolutionMode { return (isString(ref) ? containingFileMode : ref.resolutionMode) || containingFileMode; } @@ -887,7 +929,7 @@ export function isExclusivelyTypeOnlyImportOrExport(decl: ImportDeclaration | Ex * @param usage The module reference string * @returns The final resolution mode of the import */ -export function getModeForUsageLocation(file: { impliedNodeFormat?: ResolutionMode }, usage: StringLiteralLike) { +export function getModeForUsageLocation(file: { impliedNodeFormat?: ResolutionMode }, usage: StringLiteralLike): ResolutionMode { if (file.impliedNodeFormat === undefined) return undefined; if ((isImportDeclaration(usage.parent) || isExportDeclaration(usage.parent))) { const isTypeOnly = isExclusivelyTypeOnlyImportOrExport(usage.parent); @@ -940,12 +982,6 @@ const emptyResolution: ResolvedModuleWithFailedLookupLocations & ResolvedTypeRef resolvedTypeReferenceDirective: undefined, }; -/** @internal */ -export interface ResolutionNameAndModeGetter { - getName(entry: Entry): string; - getMode(entry: Entry, file: SourceFile): ResolutionMode; -} - /** @internal */ export interface ResolutionLoader { @@ -1056,6 +1092,13 @@ export function loadWithModeAwareCache( resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined, cb: (resolvedProjectReference: ResolvedProjectReference, parent: ResolvedProjectReference | undefined) => T | undefined +): T | undefined { + return forEachResolvedProjectReferenceWorker(resolvedProjectReferences, cb); +} + +function forEachResolvedProjectReferenceWorker( + resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined, + cb: (resolvedProjectReference: ResolvedProjectReference, parent: ResolvedProjectReference | undefined) => T | undefined ): T | undefined { return forEachProjectReference(/*projectReferences*/ undefined, resolvedProjectReferences, (resolvedRef, parent) => resolvedRef && cb(resolvedRef, parent)); } @@ -1673,7 +1716,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg compilerHost: host, getSymlinkCache, useSourceOfProjectReferenceRedirect, - toPath, + toPath: toPathWorker, getResolvedProjectReferences, getSourceOfProjectReferenceRedirect, forEachResolvedProjectReference @@ -1810,7 +1853,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg (oldResolvedRef, parent, index) => { const oldReference = parent?.commandLine.projectReferences![index] || oldProgram!.getProjectReferences()![index]; const oldRefPath = resolveProjectReferencePath(oldReference); - if (!projectReferenceRedirects?.has(toPath(oldRefPath))) { + if (!projectReferenceRedirects?.has(toPathWorker(oldRefPath))) { host.onReleaseParsedCommandLine!(oldRefPath, oldResolvedRef, oldProgram!.getCompilerOptions()); } } @@ -1836,12 +1879,12 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg getSemanticDiagnostics, getCachedSemanticDiagnostics, getSuggestionDiagnostics, - getDeclarationDiagnostics, + getDeclarationDiagnostics: getDeclarationDiagnosticsForFileWorker, getBindAndCheckDiagnostics, getProgramDiagnostics, getTypeChecker, getClassifiableNames, - getCommonSourceDirectory, + getCommonSourceDirectory: getCommonSourceDirectoryWorker, emit, getCurrentDirectory: () => currentDirectory, getNodeCount: () => getTypeChecker().getNodeCount(), @@ -1973,7 +2016,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // Note:: Currently we try the real path only if the // file is from node_modules to avoid having to run real path on all file paths if (!host.realpath || !options.preserveSymlinks || !stringContains(file.originalFileName, nodeModulesPathPart)) return undefined; - const realDeclarationPath = toPath(host.realpath(file.originalFileName)); + const realDeclarationPath = toPathWorker(host.realpath(file.originalFileName)); return realDeclarationPath === file.path ? undefined : getRedirectReferenceForResolutionFromSourceOfProject(realDeclarationPath); } @@ -1985,7 +2028,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return forEachResolvedProjectReference(resolvedRef => { const out = outFile(resolvedRef.commandLine.options); if (!out) return undefined; - return toPath(out) === filePath ? resolvedRef : undefined; + return toPathWorker(out) === filePath ? resolvedRef : undefined; }); } @@ -2004,14 +2047,14 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return libs.length + 2; } - function toPath(fileName: string): Path { - return ts_toPath(fileName, currentDirectory, getCanonicalFileName); + function toPathWorker(fileName: string): Path { + return toPath(fileName, currentDirectory, getCanonicalFileName); } - function getCommonSourceDirectory() { + function getCommonSourceDirectoryWorker() { if (commonSourceDirectory === undefined) { const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, program)); - commonSourceDirectory = ts_getCommonSourceDirectory( + commonSourceDirectory = getCommonSourceDirectory( options, () => mapDefined(emittedFiles, file => file.isDeclarationFile ? undefined : file.fileName), currentDirectory, @@ -2214,7 +2257,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg const containingSourceFile = !isString(containingFile) ? containingFile : undefined; const canReuseResolutions = !isString(containingFile) ? containingFile === oldSourceFile && !hasInvalidatedResolutions(oldSourceFile.path) : - !hasInvalidatedResolutions(toPath(containingFile)); + !hasInvalidatedResolutions(toPathWorker(containingFile)); for (let i = 0; i < typeDirectiveNames.length; i++) { const entry = typeDirectiveNames[i]; if (canReuseResolutions) { @@ -2561,7 +2604,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg readFile: f => host.readFile(f), fileExists: f => { // Use local caches - const path = toPath(f); + const path = toPathWorker(f); if (getSourceFileByPath(path)) return true; if (contains(missingFilePaths, path)) return false; // Before falling back to the host @@ -2619,7 +2662,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg projectReferences, (_ref, index) => resolvedProjectReferences![index]?.commandLine, fileName => { - const path = toPath(fileName); + const path = toPathWorker(fileName); const sourceFile = getSourceFileByPath(path); return sourceFile ? sourceFile.text : filesByName.has(path) ? undefined : host.readFile(path); }, @@ -2667,7 +2710,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } function isEmitBlocked(emitFileName: string): boolean { - return hasEmitBlockingDiagnostics.has(toPath(emitFileName)); + return hasEmitBlockingDiagnostics.has(toPathWorker(emitFileName)); } function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnly?: boolean | EmitOnly, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult { @@ -2704,7 +2747,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } function getSourceFile(fileName: string): SourceFile | undefined { - return getSourceFileByPath(toPath(fileName)); + return getSourceFileByPath(toPathWorker(fileName)); } function getSourceFileByPath(path: Path): SourceFile | undefined { @@ -2757,7 +2800,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, programDiagnosticsInFile).diagnostics; } - function getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] { + function getDeclarationDiagnosticsForFileWorker(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] { const options = program.getCompilerOptions(); // collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit) if (!sourceFile || outFile(options)) { @@ -3152,7 +3195,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg return runWithCancellationToken(() => { const resolver = getTypeChecker().getEmitResolver(sourceFile, cancellationToken); // Don't actually write any files since we're just getting diagnostics. - return ts_getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile) || emptyArray; + return getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile) || emptyArray; }); } @@ -3489,7 +3532,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } function findSourceFileWorker(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined { - const path = toPath(fileName); + const path = toPathWorker(fileName); if (useSourceOfProjectReferenceRedirect) { let source = getSourceOfProjectReferenceRedirect(path); // If preserveSymlinks is true, module resolution wont jump the symlink @@ -3501,7 +3544,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg options.preserveSymlinks && isDeclarationFileName(fileName) && stringContains(fileName, nodeModulesPathPart)) { - const realPath = toPath(host.realpath(fileName)); + const realPath = toPathWorker(host.realpath(fileName)); if (realPath !== path) source = getSourceOfProjectReferenceRedirect(realPath); } if (source) { @@ -3520,7 +3563,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected if (file && !(options.forceConsistentCasingInFileNames === false)) { const checkedName = file.fileName; - const isRedirect = toPath(checkedName) !== toPath(fileName); + const isRedirect = toPathWorker(checkedName) !== toPathWorker(fileName); if (isRedirect) { fileName = getProjectReferenceRedirect(fileName) || fileName; } @@ -3573,7 +3616,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // end up trying to add it to the program *again* because we were tracking it via its // original (un-redirected) name. So we have to map both the original path and the redirected path // to the source file we're about to find/create - redirectedPath = toPath(redirect); + redirectedPath = toPathWorker(redirect); } } @@ -3583,7 +3626,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg fileName, sourceFileOptions, hostErrorMessage => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_read_file_0_Colon_1, [fileName, hostErrorMessage]), - shouldCreateNewSourceFile || (oldProgram?.getSourceFileByPath(toPath(fileName))?.impliedNodeFormat !== sourceFileOptions.impliedNodeFormat) + shouldCreateNewSourceFile || (oldProgram?.getSourceFileByPath(toPathWorker(fileName))?.impliedNodeFormat !== sourceFileOptions.impliedNodeFormat) ); if (packageId) { @@ -3592,7 +3635,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg if (fileFromPackageId) { // Some other SourceFile already exists with this package name and version. // Instead of creating a duplicate, just redirect to the existing one. - const dupFile = createRedirectedSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName, sourceFileOptions); + const dupFile = createRedirectedSourceFile(fileFromPackageId, file!, fileName, path, toPathWorker(fileName), originalFileName, sourceFileOptions); redirectTargetsMap.add(fileFromPackageId.path, fileName); addFileToFilesByName(dupFile, path, redirectedPath); addFileIncludeReason(dupFile, reason); @@ -3612,7 +3655,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); file.fileName = fileName; // Ensure that source file has same name as what we were looking for file.path = path; - file.resolvedPath = toPath(fileName); + file.resolvedPath = toPathWorker(fileName); file.originalFileName = originalFileName; file.packageJsonLocations = sourceFileOptions.packageJsonLocations?.length ? sourceFileOptions.packageJsonLocations : undefined; file.packageJsonScope = sourceFileOptions.packageJsonScope; @@ -3700,21 +3743,21 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg mapFromFileToProjectReferenceRedirects = new Map(); forEachResolvedProjectReference(referencedProject => { // not input file from the referenced project, ignore - if (toPath(options.configFilePath!) !== referencedProject.sourceFile.path) { + if (toPathWorker(options.configFilePath!) !== referencedProject.sourceFile.path) { referencedProject.commandLine.fileNames.forEach(f => - mapFromFileToProjectReferenceRedirects!.set(toPath(f), referencedProject.sourceFile.path)); + mapFromFileToProjectReferenceRedirects!.set(toPathWorker(f), referencedProject.sourceFile.path)); } }); } - const referencedProjectPath = mapFromFileToProjectReferenceRedirects.get(toPath(fileName)); + const referencedProjectPath = mapFromFileToProjectReferenceRedirects.get(toPathWorker(fileName)); return referencedProjectPath && getResolvedProjectReferenceByPath(referencedProjectPath); } function forEachResolvedProjectReference( cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined ): T | undefined { - return ts_forEachResolvedProjectReference(resolvedProjectReferences, cb); + return forEachResolvedProjectReferenceWorker(resolvedProjectReferences, cb); } function getSourceOfProjectReferenceRedirect(path: Path) { @@ -3726,14 +3769,14 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg if (out) { // Dont know which source file it means so return true? const outputDts = changeExtension(out, Extension.Dts); - mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true); + mapFromToProjectReferenceRedirectSource!.set(toPathWorker(outputDts), true); } else { const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(resolvedRef.commandLine, !host.useCaseSensitiveFileNames())); forEach(resolvedRef.commandLine.fileNames, fileName => { if (!isDeclarationFileName(fileName) && !fileExtensionIs(fileName, Extension.Json)) { const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory); - mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName); + mapFromToProjectReferenceRedirectSource!.set(toPathWorker(outputDts), fileName); } }); } @@ -4036,7 +4079,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // The actual filename (i.e. add "/tsconfig.json" if necessary) const refPath = resolveProjectReferencePath(ref); - const sourceFilePath = toPath(refPath); + const sourceFilePath = toPathWorker(refPath); const fromCache = projectReferenceRedirects.get(sourceFilePath); if (fromCache !== undefined) { return fromCache || undefined; @@ -4130,7 +4173,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // List of collected files is complete; validate exhautiveness if this is a project with a file list if (options.composite) { - const rootPaths = new Set(rootNames.map(toPath)); + const rootPaths = new Set(rootNames.map(toPathWorker)); for (const file of files) { // Ignore file that is not emitted if (sourceFileMayBeEmitted(file, program) && !rootPaths.has(file.path)) { @@ -4264,7 +4307,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg options.mapRoot) { // there is --mapRoot specified // Precalculate and cache the common source directory - const dir = getCommonSourceDirectory(); + const dir = getCommonSourceDirectoryWorker(); // If we failed to find a good common directory, but outDir is specified and at least one of our files is on a windows drive/URL/other resource, add a failure if (options.outDir && dir === "" && files.some(file => getRootLength(file.fileName) > 1)) { @@ -4385,7 +4428,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // Verify that all the emit files are unique and don't overwrite input files function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Set) { if (emitFileName) { - const emitFilePath = toPath(emitFileName); + const emitFilePath = toPathWorker(emitFileName); // Report error if the output overwrites input file if (filesByName.has(emitFilePath)) { let chain: DiagnosticMessageChain | undefined; @@ -4685,7 +4728,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } if (!parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath(options)) { createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoPath, ref.path); - hasEmitBlockingDiagnostics.set(toPath(buildInfoPath), true); + hasEmitBlockingDiagnostics.set(toPathWorker(buildInfoPath), true); } }); } @@ -4828,7 +4871,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) { - hasEmitBlockingDiagnostics.set(toPath(emitFileName), true); + hasEmitBlockingDiagnostics.set(toPathWorker(emitFileName), true); programDiagnostics.add(diag); } @@ -4838,7 +4881,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg } // If this is source file, its not emitted file - const filePath = toPath(file); + const filePath = toPathWorker(file); if (getSourceFileByPath(filePath)) { return false; } @@ -5235,3 +5278,59 @@ export function getModuleNameStringLiteralAt({ imports, moduleAugmentations }: S } Debug.fail("should never ask for module name at index higher than possible module name"); } + +/** @internal */ +export function getSetExternalModuleIndicator(options: CompilerOptions): (file: SourceFile) => void { + // TODO: Should this callback be cached? + switch (getEmitModuleDetectionKind(options)) { + case ModuleDetectionKind.Force: + // All non-declaration files are modules, declaration files still do the usual isFileProbablyExternalModule + return (file: SourceFile) => { + file.externalModuleIndicator = isFileProbablyExternalModule(file) || !file.isDeclarationFile || undefined; + }; + case ModuleDetectionKind.Legacy: + // Files are modules if they have imports, exports, or import.meta + return (file: SourceFile) => { + file.externalModuleIndicator = isFileProbablyExternalModule(file); + }; + case ModuleDetectionKind.Auto: + // If module is nodenext or node16, all esm format files are modules + // If jsx is react-jsx or react-jsxdev then jsx tags force module-ness + // otherwise, the presence of import or export statments (or import.meta) implies module-ness + const checks: ((file: SourceFile) => Node | true | undefined)[] = [isFileProbablyExternalModule]; + if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) { + checks.push(isFileModuleFromUsingJSXTag); + } + checks.push(isFileForcedToBeModuleByFormat); + const combined = or(...checks); + const callback = (file: SourceFile) => void (file.externalModuleIndicator = combined(file)); + return callback; + } +} + +/** + * This is a somewhat unavoidable full tree walk to locate a JSX tag - `import.meta` requires the same, + * but we avoid that walk (or parts of it) if at all possible using the `PossiblyContainsImportMeta` node flag. + * Unfortunately, there's no `NodeFlag` space to do the same for JSX. + */ +function walkTreeForJSXTags(node: Node): Node | undefined { + if (!(node.transformFlags & TransformFlags.ContainsJsx)) return undefined; + return isJsxOpeningLikeElement(node) || isJsxFragment(node) ? node : forEachChild(node, walkTreeForJSXTags); +} + +function isFileModuleFromUsingJSXTag(file: SourceFile): Node | undefined { + // Excludes declaration files - they still require an explicit `export {}` or the like + // for back compat purposes. (not that declaration files should contain JSX tags!) + return !file.isDeclarationFile ? walkTreeForJSXTags(file) : undefined; +} + +/** + * Note that this requires file.impliedNodeFormat be set already; meaning it must be set very early on + * in SourceFile construction. + */ +function isFileForcedToBeModuleByFormat(file: SourceFile): true | undefined { + // Excludes declaration files - they still require an explicit `export {}` or the like + // for back compat purposes. The only non-declaration files _not_ forced to be a module are `.js` files + // that aren't esm-mode (meaning not in a `type: module` scope). + return (file.impliedNodeFormat === ModuleKind.ESNext || (fileExtensionIsOneOf(file.fileName, [Extension.Cjs, Extension.Cts, Extension.Mjs, Extension.Mts]))) && !file.isDeclarationFile ? true : undefined; +} diff --git a/src/compiler/programUtilities.ts b/src/compiler/programUtilities.ts new file mode 100644 index 0000000000000..017d18a9b4502 --- /dev/null +++ b/src/compiler/programUtilities.ts @@ -0,0 +1,96 @@ +import { + affectsDeclarationPathOptionDeclarations, + affectsEmitOptionDeclarations, + moduleResolutionOptionDeclarations, + optionsAffectingProgramStructure, + semanticDiagnosticsOptionDeclarations, +} from "./commandLineParser"; +import * as Debug from "./debug"; +import { + createModeAwareCache, + ModeAwareCache, +} from "./moduleNameResolver"; +import { + CompilerOptions, + ResolutionMode, + ResolutionNameAndModeGetter, + ResolvedModuleWithFailedLookupLocations, + ResolvedTypeReferenceDirectiveWithFailedLookupLocations, + SourceFile, +} from "./types"; +import { optionsHaveChanges } from "./utilities"; + +/** @internal */ +export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleWithFailedLookupLocations, mode: ResolutionMode): void { + if (!sourceFile.resolvedModules) { + sourceFile.resolvedModules = createModeAwareCache(); + } + sourceFile.resolvedModules.set(moduleNameText, mode, resolvedModule); +} + +/** @internal */ +export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirectiveWithFailedLookupLocations, mode: ResolutionMode): void { + if (!sourceFile.resolvedTypeReferenceDirectiveNames) { + sourceFile.resolvedTypeReferenceDirectiveNames = createModeAwareCache(); + } + sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, mode, resolvedTypeReferenceDirective); +} + +/** @internal */ +export function hasChangesInResolutions( + names: readonly K[], + newSourceFile: SourceFile, + newResolutions: readonly V[], + oldResolutions: ModeAwareCache | undefined, + comparer: (oldResolution: V, newResolution: V) => boolean, + nameAndModeGetter: ResolutionNameAndModeGetter, +): boolean { + Debug.assert(names.length === newResolutions.length); + + for (let i = 0; i < names.length; i++) { + const newResolution = newResolutions[i]; + const entry = names[i]; + const name = nameAndModeGetter.getName(entry); + const mode = nameAndModeGetter.getMode(entry, newSourceFile); + const oldResolution = oldResolutions && oldResolutions.get(name, mode); + const changed = + oldResolution + ? !newResolution || !comparer(oldResolution, newResolution) + : newResolution; + if (changed) { + return true; + } + } + return false; +} + +/** @internal */ +export function changesAffectModuleResolution(oldOptions: CompilerOptions, newOptions: CompilerOptions): boolean { + return oldOptions.configFilePath !== newOptions.configFilePath || + optionsHaveModuleResolutionChanges(oldOptions, newOptions); +} + +/** @internal */ +export function optionsHaveModuleResolutionChanges(oldOptions: CompilerOptions, newOptions: CompilerOptions) { + return optionsHaveChanges(oldOptions, newOptions, moduleResolutionOptionDeclarations); +} + +/** @internal */ +export function changesAffectingProgramStructure(oldOptions: CompilerOptions, newOptions: CompilerOptions) { + return optionsHaveChanges(oldOptions, newOptions, optionsAffectingProgramStructure); +} + +/** @internal */ +export function compilerOptionsAffectSemanticDiagnostics(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean { + return optionsHaveChanges(oldOptions, newOptions, semanticDiagnosticsOptionDeclarations); +} + +/** @internal */ +export function compilerOptionsAffectEmit(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean { + return optionsHaveChanges(oldOptions, newOptions, affectsEmitOptionDeclarations); +} + +/** @internal */ +export function compilerOptionsAffectDeclarationPath(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean { + return optionsHaveChanges(oldOptions, newOptions, affectsDeclarationPathOptionDeclarations); +} diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 31c0818e1280b..d724aff87d5e1 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -1,83 +1,99 @@ import { arrayToMap, - CachedDirectoryStructureHost, - clearMap, - closeFileWatcher, - closeFileWatcherOf, - CompilerOptions, - createModeAwareCache, - createModuleResolutionCache, createMultiMap, - createTypeReferenceDirectiveResolutionCache, - createTypeReferenceResolutionLoader, - Debug, - Diagnostics, - directorySeparator, - DirectoryWatcherCallback, emptyArray, endsWith, - Extension, - extensionIsTS, - fileExtensionIs, - FileReference, - FileWatcher, - FileWatcherCallback, firstDefinedIterator, GetCanonicalFileName, - getDirectoryPath, + memoize, + removeSuffix, + returnTrue, + some, + startsWith, + stringContains, +} from "./core"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { + extensionIsTS, + resolutionExtensionIsTSOrJson, +} from "./extension"; +import { + createModeAwareCache, + createModuleResolutionCache, + createTypeReferenceDirectiveResolutionCache, getEffectiveTypeRoots, - getInferredLibraryNameResolveFrom, - getNormalizedAbsolutePath, getOptionsForLibraryResolution, + isTraceEnabled, + loadModuleFromGlobalCache, + ModeAwareCache, + ModuleResolutionCache, + parseNodeModuleFromPath, + resolveLibrary as ts_resolveLibrary, + resolveModuleName, + trace, + updateResolutionField, +} from "./moduleNameResolver"; +import { + directorySeparator, + fileExtensionIs, + getDirectoryPath, + getNormalizedAbsolutePath, getPathComponents, getPathFromPathComponents, - HasInvalidatedLibResolutions, - HasInvalidatedResolutions, hasTrailingDirectorySeparator, - ignoredPaths, - inferredTypesContainingFile, isDiskPathRoot, - isEmittedFileOfProgram, - isExternalModuleNameRelative, - isExternalOrCommonJsModule, isNodeModulesDirectory, isRootedDiskPath, - isTraceEnabled, - loadModuleFromGlobalCache, - memoize, - MinimalResolutionCacheHost, - ModeAwareCache, - ModuleResolutionCache, - moduleResolutionNameAndModeGetter, - mutateMap, - noopFileWatcher, normalizePath, - PackageId, - packageIdToString, - parseNodeModuleFromPath, - Path, PathPathComponents, - Program, - removeSuffix, removeTrailingDirectorySeparator, - resolutionExtensionIsTSOrJson, +} from "./path"; +import { + createTypeReferenceResolutionLoader, + getInferredLibraryNameResolveFrom, + inferredTypesContainingFile, + moduleResolutionNameAndModeGetter, ResolutionLoader, +} from "./program"; +import { ignoredPaths } from "./sysUtilities"; +import { + CompilerOptions, + DirectoryWatcherCallback, + Extension, + FileReference, + FileWatcher, + FileWatcherCallback, + HasInvalidatedLibResolutions, + HasInvalidatedResolutions, + MinimalResolutionCacheHost, + PackageId, + Path, + Program, ResolutionMode, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirectiveWithFailedLookupLocations, - resolveLibrary as ts_resolveLibrary, - resolveModuleName as ts_resolveModuleName, - returnTrue, - some, SourceFile, - startsWith, - stringContains, StringLiteralLike, - trace, - updateResolutionField, WatchDirectoryFlags, -} from "./_namespaces/ts"; +} from "./types"; +import { + clearMap, + closeFileWatcher, + isExternalOrCommonJsModule, + mutateMap, + packageIdToString, +} from "./utilities"; +import { + isExternalModuleNameRelative, +} from "./utilitiesPublic"; +import { noopFileWatcher } from "./watch"; +import { + CachedDirectoryStructureHost, + closeFileWatcherOf, + isEmittedFileOfProgram, +} from "./watchUtilities"; /** @internal */ export interface HasInvalidatedFromResolutionCache { @@ -644,9 +660,9 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD hasChangedAutomaticTypeDirectiveNames = false; } - function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, redirectedReference?: ResolvedProjectReference, mode?: ResolutionMode): CachedResolvedModuleWithFailedLookupLocations { + function resolveModuleNameWorker(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, redirectedReference?: ResolvedProjectReference, mode?: ResolutionMode): CachedResolvedModuleWithFailedLookupLocations { const host = resolutionHost.getCompilerHost?.() || resolutionHost; - const primaryResult = ts_resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache, redirectedReference, mode); + const primaryResult = resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache, redirectedReference, mode); // return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts if (!resolutionHost.getGlobalCache) { return primaryResult; @@ -686,7 +702,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD ): ResolutionLoader { return { nameAndMode: moduleResolutionNameAndModeGetter, - resolve: (moduleName, resoluionMode) => resolveModuleName( + resolve: (moduleName, resoluionMode) => resolveModuleNameWorker( moduleName, containingFile, options, @@ -918,7 +934,7 @@ export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootD const resolutionsInFile = resolvedModuleNames.get(path); const resolution = resolutionsInFile?.get(moduleName, /*mode*/ undefined); if (resolution && !resolution.isInvalidated) return resolution; - return resolveModuleName(moduleName, containingFile, resolutionHost.getCompilationSettings()); + return resolveModuleNameWorker(moduleName, containingFile, resolutionHost.getCompilationSettings()); } function isNodeModulesAtTypesDirectory(dirPath: Path) { diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 9f539aec49d80..3f854cf0b2f7f 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -2,32 +2,39 @@ import { append, arraysEqual, binarySearch, + compareValues, + identity, + isLineBreak, + isWhiteSpaceLike, + isWhiteSpaceSingleLine, + padLeft, + trimStringStart, +} from "./core"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { textToKeyword, textToToken } from "./scannerKeywords"; +import { + parsePseudoBigInt, + positionIsSynthesized, +} from "./scannerUtilities"; +import { CharacterCodes, CommentDirective, CommentDirectiveType, CommentKind, CommentRange, - compareValues, - Debug, DiagnosticMessage, - Diagnostics, - identity, JSDocSyntaxKind, JsxTokenSyntaxKind, KeywordSyntaxKind, LanguageVariant, LineAndCharacter, - MapLike, - padLeft, - parsePseudoBigInt, - positionIsSynthesized, PunctuationOrKeywordSyntaxKind, ScriptTarget, SourceFileLike, SyntaxKind, TokenFlags, - trimStringStart, -} from "./_namespaces/ts"; +} from "./types"; export type ErrorCallback = (message: DiagnosticMessage, length: number, arg0?: any) => void; @@ -118,159 +125,6 @@ export interface Scanner { tryScan(callback: () => T): T; } -/** @internal */ -export const textToKeywordObj: MapLike = { - abstract: SyntaxKind.AbstractKeyword, - accessor: SyntaxKind.AccessorKeyword, - any: SyntaxKind.AnyKeyword, - as: SyntaxKind.AsKeyword, - asserts: SyntaxKind.AssertsKeyword, - assert: SyntaxKind.AssertKeyword, - bigint: SyntaxKind.BigIntKeyword, - boolean: SyntaxKind.BooleanKeyword, - break: SyntaxKind.BreakKeyword, - case: SyntaxKind.CaseKeyword, - catch: SyntaxKind.CatchKeyword, - class: SyntaxKind.ClassKeyword, - continue: SyntaxKind.ContinueKeyword, - const: SyntaxKind.ConstKeyword, - ["" + "constructor"]: SyntaxKind.ConstructorKeyword, - debugger: SyntaxKind.DebuggerKeyword, - declare: SyntaxKind.DeclareKeyword, - default: SyntaxKind.DefaultKeyword, - delete: SyntaxKind.DeleteKeyword, - do: SyntaxKind.DoKeyword, - else: SyntaxKind.ElseKeyword, - enum: SyntaxKind.EnumKeyword, - export: SyntaxKind.ExportKeyword, - extends: SyntaxKind.ExtendsKeyword, - false: SyntaxKind.FalseKeyword, - finally: SyntaxKind.FinallyKeyword, - for: SyntaxKind.ForKeyword, - from: SyntaxKind.FromKeyword, - function: SyntaxKind.FunctionKeyword, - get: SyntaxKind.GetKeyword, - if: SyntaxKind.IfKeyword, - implements: SyntaxKind.ImplementsKeyword, - import: SyntaxKind.ImportKeyword, - in: SyntaxKind.InKeyword, - infer: SyntaxKind.InferKeyword, - instanceof: SyntaxKind.InstanceOfKeyword, - interface: SyntaxKind.InterfaceKeyword, - intrinsic: SyntaxKind.IntrinsicKeyword, - is: SyntaxKind.IsKeyword, - keyof: SyntaxKind.KeyOfKeyword, - let: SyntaxKind.LetKeyword, - module: SyntaxKind.ModuleKeyword, - namespace: SyntaxKind.NamespaceKeyword, - never: SyntaxKind.NeverKeyword, - new: SyntaxKind.NewKeyword, - null: SyntaxKind.NullKeyword, - number: SyntaxKind.NumberKeyword, - object: SyntaxKind.ObjectKeyword, - package: SyntaxKind.PackageKeyword, - private: SyntaxKind.PrivateKeyword, - protected: SyntaxKind.ProtectedKeyword, - public: SyntaxKind.PublicKeyword, - override: SyntaxKind.OverrideKeyword, - out: SyntaxKind.OutKeyword, - readonly: SyntaxKind.ReadonlyKeyword, - require: SyntaxKind.RequireKeyword, - global: SyntaxKind.GlobalKeyword, - return: SyntaxKind.ReturnKeyword, - satisfies: SyntaxKind.SatisfiesKeyword, - set: SyntaxKind.SetKeyword, - static: SyntaxKind.StaticKeyword, - string: SyntaxKind.StringKeyword, - super: SyntaxKind.SuperKeyword, - switch: SyntaxKind.SwitchKeyword, - symbol: SyntaxKind.SymbolKeyword, - this: SyntaxKind.ThisKeyword, - throw: SyntaxKind.ThrowKeyword, - true: SyntaxKind.TrueKeyword, - try: SyntaxKind.TryKeyword, - type: SyntaxKind.TypeKeyword, - typeof: SyntaxKind.TypeOfKeyword, - undefined: SyntaxKind.UndefinedKeyword, - unique: SyntaxKind.UniqueKeyword, - unknown: SyntaxKind.UnknownKeyword, - var: SyntaxKind.VarKeyword, - void: SyntaxKind.VoidKeyword, - while: SyntaxKind.WhileKeyword, - with: SyntaxKind.WithKeyword, - yield: SyntaxKind.YieldKeyword, - async: SyntaxKind.AsyncKeyword, - await: SyntaxKind.AwaitKeyword, - of: SyntaxKind.OfKeyword, -}; - -const textToKeyword = new Map(Object.entries(textToKeywordObj)); - -const textToToken = new Map(Object.entries({ - ...textToKeywordObj, - "{": SyntaxKind.OpenBraceToken, - "}": SyntaxKind.CloseBraceToken, - "(": SyntaxKind.OpenParenToken, - ")": SyntaxKind.CloseParenToken, - "[": SyntaxKind.OpenBracketToken, - "]": SyntaxKind.CloseBracketToken, - ".": SyntaxKind.DotToken, - "...": SyntaxKind.DotDotDotToken, - ";": SyntaxKind.SemicolonToken, - ",": SyntaxKind.CommaToken, - "<": SyntaxKind.LessThanToken, - ">": SyntaxKind.GreaterThanToken, - "<=": SyntaxKind.LessThanEqualsToken, - ">=": SyntaxKind.GreaterThanEqualsToken, - "==": SyntaxKind.EqualsEqualsToken, - "!=": SyntaxKind.ExclamationEqualsToken, - "===": SyntaxKind.EqualsEqualsEqualsToken, - "!==": SyntaxKind.ExclamationEqualsEqualsToken, - "=>": SyntaxKind.EqualsGreaterThanToken, - "+": SyntaxKind.PlusToken, - "-": SyntaxKind.MinusToken, - "**": SyntaxKind.AsteriskAsteriskToken, - "*": SyntaxKind.AsteriskToken, - "/": SyntaxKind.SlashToken, - "%": SyntaxKind.PercentToken, - "++": SyntaxKind.PlusPlusToken, - "--": SyntaxKind.MinusMinusToken, - "<<": SyntaxKind.LessThanLessThanToken, - ">": SyntaxKind.GreaterThanGreaterThanToken, - ">>>": SyntaxKind.GreaterThanGreaterThanGreaterThanToken, - "&": SyntaxKind.AmpersandToken, - "|": SyntaxKind.BarToken, - "^": SyntaxKind.CaretToken, - "!": SyntaxKind.ExclamationToken, - "~": SyntaxKind.TildeToken, - "&&": SyntaxKind.AmpersandAmpersandToken, - "||": SyntaxKind.BarBarToken, - "?": SyntaxKind.QuestionToken, - "??": SyntaxKind.QuestionQuestionToken, - "?.": SyntaxKind.QuestionDotToken, - ":": SyntaxKind.ColonToken, - "=": SyntaxKind.EqualsToken, - "+=": SyntaxKind.PlusEqualsToken, - "-=": SyntaxKind.MinusEqualsToken, - "*=": SyntaxKind.AsteriskEqualsToken, - "**=": SyntaxKind.AsteriskAsteriskEqualsToken, - "/=": SyntaxKind.SlashEqualsToken, - "%=": SyntaxKind.PercentEqualsToken, - "<<=": SyntaxKind.LessThanLessThanEqualsToken, - ">>=": SyntaxKind.GreaterThanGreaterThanEqualsToken, - ">>>=": SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, - "&=": SyntaxKind.AmpersandEqualsToken, - "|=": SyntaxKind.BarEqualsToken, - "^=": SyntaxKind.CaretEqualsToken, - "||=": SyntaxKind.BarBarEqualsToken, - "&&=": SyntaxKind.AmpersandAmpersandEqualsToken, - "??=": SyntaxKind.QuestionQuestionEqualsToken, - "@": SyntaxKind.AtToken, - "#": SyntaxKind.HashToken, - "`": SyntaxKind.BacktickToken, -})); - /* As per ECMAScript Language Specification 3th Edition, Section 7.6: Identifiers IdentifierStart :: @@ -521,46 +375,6 @@ export function getLineAndCharacterOfPosition(sourceFile: SourceFileLike, positi return computeLineAndCharacterOfPosition(getLineStarts(sourceFile), position); } -export function isWhiteSpaceLike(ch: number): boolean { - return isWhiteSpaceSingleLine(ch) || isLineBreak(ch); -} - -/** Does not include line breaks. For that, see isWhiteSpaceLike. */ -export function isWhiteSpaceSingleLine(ch: number): boolean { - // Note: nextLine is in the Zs space, and should be considered to be a whitespace. - // It is explicitly not a line-break as it isn't in the exact set specified by EcmaScript. - return ch === CharacterCodes.space || - ch === CharacterCodes.tab || - ch === CharacterCodes.verticalTab || - ch === CharacterCodes.formFeed || - ch === CharacterCodes.nonBreakingSpace || - ch === CharacterCodes.nextLine || - ch === CharacterCodes.ogham || - ch >= CharacterCodes.enQuad && ch <= CharacterCodes.zeroWidthSpace || - ch === CharacterCodes.narrowNoBreakSpace || - ch === CharacterCodes.mathematicalSpace || - ch === CharacterCodes.ideographicSpace || - ch === CharacterCodes.byteOrderMark; -} - -export function isLineBreak(ch: number): boolean { - // ES5 7.3: - // The ECMAScript line terminator characters are listed in Table 3. - // Table 3: Line Terminator Characters - // Code Unit Value Name Formal Name - // \u000A Line Feed - // \u000D Carriage Return - // \u2028 Line separator - // \u2029 Paragraph separator - // Only the characters in Table 3 are treated as line terminators. Other new line or line - // breaking characters are treated as white space but not as line terminators. - - return ch === CharacterCodes.lineFeed || - ch === CharacterCodes.carriageReturn || - ch === CharacterCodes.lineSeparator || - ch === CharacterCodes.paragraphSeparator; -} - function isDigit(ch: number): boolean { return ch >= CharacterCodes._0 && ch <= CharacterCodes._9; } @@ -2823,7 +2637,6 @@ function charSize(ch: number) { // Derived from the 10.1.1 UTF16Encoding of the ES6 Spec. function utf16EncodeAsStringFallback(codePoint: number) { Debug.assert(0x0 <= codePoint && codePoint <= 0x10FFFF); - if (codePoint <= 65535) { return String.fromCharCode(codePoint); } diff --git a/src/compiler/scannerKeywords.ts b/src/compiler/scannerKeywords.ts new file mode 100644 index 0000000000000..47c724101da61 --- /dev/null +++ b/src/compiler/scannerKeywords.ts @@ -0,0 +1,157 @@ +import { MapLike } from "./corePublic"; +import { + KeywordSyntaxKind, + SyntaxKind, +} from "./types"; + +export const textToKeywordObj: MapLike = { + abstract: SyntaxKind.AbstractKeyword, + accessor: SyntaxKind.AccessorKeyword, + any: SyntaxKind.AnyKeyword, + as: SyntaxKind.AsKeyword, + asserts: SyntaxKind.AssertsKeyword, + assert: SyntaxKind.AssertKeyword, + bigint: SyntaxKind.BigIntKeyword, + boolean: SyntaxKind.BooleanKeyword, + break: SyntaxKind.BreakKeyword, + case: SyntaxKind.CaseKeyword, + catch: SyntaxKind.CatchKeyword, + class: SyntaxKind.ClassKeyword, + continue: SyntaxKind.ContinueKeyword, + const: SyntaxKind.ConstKeyword, + ["" + "constructor"]: SyntaxKind.ConstructorKeyword, + debugger: SyntaxKind.DebuggerKeyword, + declare: SyntaxKind.DeclareKeyword, + default: SyntaxKind.DefaultKeyword, + delete: SyntaxKind.DeleteKeyword, + do: SyntaxKind.DoKeyword, + else: SyntaxKind.ElseKeyword, + enum: SyntaxKind.EnumKeyword, + export: SyntaxKind.ExportKeyword, + extends: SyntaxKind.ExtendsKeyword, + false: SyntaxKind.FalseKeyword, + finally: SyntaxKind.FinallyKeyword, + for: SyntaxKind.ForKeyword, + from: SyntaxKind.FromKeyword, + function: SyntaxKind.FunctionKeyword, + get: SyntaxKind.GetKeyword, + if: SyntaxKind.IfKeyword, + implements: SyntaxKind.ImplementsKeyword, + import: SyntaxKind.ImportKeyword, + in: SyntaxKind.InKeyword, + infer: SyntaxKind.InferKeyword, + instanceof: SyntaxKind.InstanceOfKeyword, + interface: SyntaxKind.InterfaceKeyword, + intrinsic: SyntaxKind.IntrinsicKeyword, + is: SyntaxKind.IsKeyword, + keyof: SyntaxKind.KeyOfKeyword, + let: SyntaxKind.LetKeyword, + module: SyntaxKind.ModuleKeyword, + namespace: SyntaxKind.NamespaceKeyword, + never: SyntaxKind.NeverKeyword, + new: SyntaxKind.NewKeyword, + null: SyntaxKind.NullKeyword, + number: SyntaxKind.NumberKeyword, + object: SyntaxKind.ObjectKeyword, + package: SyntaxKind.PackageKeyword, + private: SyntaxKind.PrivateKeyword, + protected: SyntaxKind.ProtectedKeyword, + public: SyntaxKind.PublicKeyword, + override: SyntaxKind.OverrideKeyword, + out: SyntaxKind.OutKeyword, + readonly: SyntaxKind.ReadonlyKeyword, + require: SyntaxKind.RequireKeyword, + global: SyntaxKind.GlobalKeyword, + return: SyntaxKind.ReturnKeyword, + satisfies: SyntaxKind.SatisfiesKeyword, + set: SyntaxKind.SetKeyword, + static: SyntaxKind.StaticKeyword, + string: SyntaxKind.StringKeyword, + super: SyntaxKind.SuperKeyword, + switch: SyntaxKind.SwitchKeyword, + symbol: SyntaxKind.SymbolKeyword, + this: SyntaxKind.ThisKeyword, + throw: SyntaxKind.ThrowKeyword, + true: SyntaxKind.TrueKeyword, + try: SyntaxKind.TryKeyword, + type: SyntaxKind.TypeKeyword, + typeof: SyntaxKind.TypeOfKeyword, + undefined: SyntaxKind.UndefinedKeyword, + unique: SyntaxKind.UniqueKeyword, + unknown: SyntaxKind.UnknownKeyword, + var: SyntaxKind.VarKeyword, + void: SyntaxKind.VoidKeyword, + while: SyntaxKind.WhileKeyword, + with: SyntaxKind.WithKeyword, + yield: SyntaxKind.YieldKeyword, + async: SyntaxKind.AsyncKeyword, + await: SyntaxKind.AwaitKeyword, + of: SyntaxKind.OfKeyword, +}; + +export const textToKeyword = new Map(Object.entries(textToKeywordObj)); + +export const textToToken = new Map(Object.entries({ + ...textToKeywordObj, + "{": SyntaxKind.OpenBraceToken, + "}": SyntaxKind.CloseBraceToken, + "(": SyntaxKind.OpenParenToken, + ")": SyntaxKind.CloseParenToken, + "[": SyntaxKind.OpenBracketToken, + "]": SyntaxKind.CloseBracketToken, + ".": SyntaxKind.DotToken, + "...": SyntaxKind.DotDotDotToken, + ";": SyntaxKind.SemicolonToken, + ",": SyntaxKind.CommaToken, + "<": SyntaxKind.LessThanToken, + ">": SyntaxKind.GreaterThanToken, + "<=": SyntaxKind.LessThanEqualsToken, + ">=": SyntaxKind.GreaterThanEqualsToken, + "==": SyntaxKind.EqualsEqualsToken, + "!=": SyntaxKind.ExclamationEqualsToken, + "===": SyntaxKind.EqualsEqualsEqualsToken, + "!==": SyntaxKind.ExclamationEqualsEqualsToken, + "=>": SyntaxKind.EqualsGreaterThanToken, + "+": SyntaxKind.PlusToken, + "-": SyntaxKind.MinusToken, + "**": SyntaxKind.AsteriskAsteriskToken, + "*": SyntaxKind.AsteriskToken, + "/": SyntaxKind.SlashToken, + "%": SyntaxKind.PercentToken, + "++": SyntaxKind.PlusPlusToken, + "--": SyntaxKind.MinusMinusToken, + "<<": SyntaxKind.LessThanLessThanToken, + ">": SyntaxKind.GreaterThanGreaterThanToken, + ">>>": SyntaxKind.GreaterThanGreaterThanGreaterThanToken, + "&": SyntaxKind.AmpersandToken, + "|": SyntaxKind.BarToken, + "^": SyntaxKind.CaretToken, + "!": SyntaxKind.ExclamationToken, + "~": SyntaxKind.TildeToken, + "&&": SyntaxKind.AmpersandAmpersandToken, + "||": SyntaxKind.BarBarToken, + "?": SyntaxKind.QuestionToken, + "??": SyntaxKind.QuestionQuestionToken, + "?.": SyntaxKind.QuestionDotToken, + ":": SyntaxKind.ColonToken, + "=": SyntaxKind.EqualsToken, + "+=": SyntaxKind.PlusEqualsToken, + "-=": SyntaxKind.MinusEqualsToken, + "*=": SyntaxKind.AsteriskEqualsToken, + "**=": SyntaxKind.AsteriskAsteriskEqualsToken, + "/=": SyntaxKind.SlashEqualsToken, + "%=": SyntaxKind.PercentEqualsToken, + "<<=": SyntaxKind.LessThanLessThanEqualsToken, + ">>=": SyntaxKind.GreaterThanGreaterThanEqualsToken, + ">>>=": SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, + "&=": SyntaxKind.AmpersandEqualsToken, + "|=": SyntaxKind.BarEqualsToken, + "^=": SyntaxKind.CaretEqualsToken, + "||=": SyntaxKind.BarBarEqualsToken, + "&&=": SyntaxKind.AmpersandAmpersandEqualsToken, + "??=": SyntaxKind.QuestionQuestionEqualsToken, + "@": SyntaxKind.AtToken, + "#": SyntaxKind.HashToken, + "`": SyntaxKind.BacktickToken, +})); diff --git a/src/compiler/scannerUtilities.ts b/src/compiler/scannerUtilities.ts new file mode 100644 index 0000000000000..5ce6ffc28dfdc --- /dev/null +++ b/src/compiler/scannerUtilities.ts @@ -0,0 +1,81 @@ +import { CharacterCodes } from "./types"; + +/** @internal */ +export function positionIsSynthesized(pos: number): boolean { + // This is a fast way of testing the following conditions: + // pos === undefined || pos === null || isNaN(pos) || pos < 0; + return !(pos >= 0); +} + +/** + * Converts a bigint literal string, e.g. `0x1234n`, + * to its decimal string representation, e.g. `4660`. + * + * @internal + */ +export function parsePseudoBigInt(stringValue: string): string { + let log2Base: number; + switch (stringValue.charCodeAt(1)) { // "x" in "0x123" + case CharacterCodes.b: + case CharacterCodes.B: // 0b or 0B + log2Base = 1; + break; + case CharacterCodes.o: + case CharacterCodes.O: // 0o or 0O + log2Base = 3; + break; + case CharacterCodes.x: + case CharacterCodes.X: // 0x or 0X + log2Base = 4; + break; + default: // already in decimal; omit trailing "n" + const nIndex = stringValue.length - 1; + // Skip leading 0s + let nonZeroStart = 0; + while (stringValue.charCodeAt(nonZeroStart) === CharacterCodes._0) { + nonZeroStart++; + } + return stringValue.slice(nonZeroStart, nIndex) || "0"; + } + + // Omit leading "0b", "0o", or "0x", and trailing "n" + const startIndex = 2, endIndex = stringValue.length - 1; + const bitsNeeded = (endIndex - startIndex) * log2Base; + // Stores the value specified by the string as a LE array of 16-bit integers + // using Uint16 instead of Uint32 so combining steps can use bitwise operators + const segments = new Uint16Array((bitsNeeded >>> 4) + (bitsNeeded & 15 ? 1 : 0)); + // Add the digits, one at a time + for (let i = endIndex - 1, bitOffset = 0; i >= startIndex; i--, bitOffset += log2Base) { + const segment = bitOffset >>> 4; + const digitChar = stringValue.charCodeAt(i); + // Find character range: 0-9 < A-F < a-f + const digit = digitChar <= CharacterCodes._9 + ? digitChar - CharacterCodes._0 + : 10 + digitChar - + (digitChar <= CharacterCodes.F ? CharacterCodes.A : CharacterCodes.a); + const shiftedDigit = digit << (bitOffset & 15); + segments[segment] |= shiftedDigit; + const residual = shiftedDigit >>> 16; + if (residual) segments[segment + 1] |= residual; // overflows segment + } + // Repeatedly divide segments by 10 and add remainder to base10Value + let base10Value = ""; + let firstNonzeroSegment = segments.length - 1; + let segmentsRemaining = true; + while (segmentsRemaining) { + let mod10 = 0; + segmentsRemaining = false; + for (let segment = firstNonzeroSegment; segment >= 0; segment--) { + const newSegment = mod10 << 16 | segments[segment]; + const segmentValue = (newSegment / 10) | 0; + segments[segment] = segmentValue; + mod10 = newSegment - segmentValue * 10; + if (segmentValue && !segmentsRemaining) { + firstNonzeroSegment = segment; + segmentsRemaining = true; + } + } + base10Value = mod10 + base10Value; + } + return base10Value; +} diff --git a/src/compiler/semver.ts b/src/compiler/semver.ts index b9c5864c0c042..42bbd769a4aaf 100644 --- a/src/compiler/semver.ts +++ b/src/compiler/semver.ts @@ -1,15 +1,15 @@ import { compareStringsCaseSensitive, compareValues, - Comparison, - Debug, emptyArray, every, isArray, map, some, trimString, -} from "./_namespaces/ts"; +} from "./core"; +import { Comparison } from "./corePublic"; +import * as Debug from "./debug"; // https://semver.org/#spec-item-2 // > A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative @@ -204,7 +204,11 @@ export class VersionRange { private _alternatives: readonly (readonly Comparator[])[]; constructor(spec: string) { - this._alternatives = spec ? Debug.checkDefined(parseRange(spec), "Invalid range spec.") : emptyArray; + const result = spec ? parseRange(spec) : emptyArray; + if (result === undefined) { + throw new Error("Invalid range spec."); + } + this._alternatives = result; } static tryParse(text: string) { diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 78ebf13ba0878..8f0b30034da1d 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -1,32 +1,36 @@ import { arrayFrom, binarySearchKey, - CharacterCodes, - combinePaths, compareValues, - Debug, - DocumentPosition, - DocumentPositionMapper, - DocumentPositionMapperHost, - EmitHost, emptyArray, every, - getDirectoryPath, - getNormalizedAbsolutePath, - getPositionOfLineAndCharacter, - getRelativePathToDirectoryOrUrl, identity, isArray, isString, - LineAndCharacter, - RawSourceMap, some, sortAndDeduplicate, - SortedReadonlyArray, - SourceMapGenerator, trimStringEnd, -} from "./_namespaces/ts"; -import * as performance from "./_namespaces/ts.performance"; +} from "./core"; +import { SortedReadonlyArray } from "./corePublic"; +import * as Debug from "./debug"; +import { + combinePaths, + getDirectoryPath, + getNormalizedAbsolutePath, + getRelativePathToDirectoryOrUrl, +} from "./path"; +import * as performance from "./performance"; +import { getPositionOfLineAndCharacter } from "./scanner"; +import { + CharacterCodes, + DocumentPosition, + DocumentPositionMapper, + DocumentPositionMapperHost, + EmitHost, + LineAndCharacter, + RawSourceMap, + SourceMapGenerator, +} from "./types"; /** @internal */ export interface SourceMapGeneratorOptions { @@ -40,7 +44,7 @@ export function createSourceMapGenerator(host: EmitHost, file: string, sourceRoo /* eslint-disable no-var */ var { enter, exit } = generatorOptions.extendedDiagnostics ? performance.createTimer("Source Map", "beforeSourcemap", "afterSourcemap") - : performance.nullTimer; + : performance.createNullTimer(); // Current source map file and its index in the sources list var rawSources: string[] = []; diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts index 09853623cc5db..234a4f951b192 100644 --- a/src/compiler/symbolWalker.ts +++ b/src/compiler/symbolWalker.ts @@ -1,10 +1,11 @@ import { - BaseType, clear, - EntityNameOrEntityNameExpression, forEach, getOwnValues, - getSymbolId, +} from "./core"; +import { + BaseType, + EntityNameOrEntityNameExpression, Identifier, IndexedAccessType, IndexType, @@ -24,7 +25,8 @@ import { TypeQueryNode, TypeReference, UnionOrIntersectionType, -} from "./_namespaces/ts"; +} from "./types"; +import { getSymbolId } from "./utilities"; /** @internal */ export function createGetSymbolWalker( diff --git a/src/compiler/symlinkCache.ts b/src/compiler/symlinkCache.ts new file mode 100644 index 0000000000000..10a479cfef8f7 --- /dev/null +++ b/src/compiler/symlinkCache.ts @@ -0,0 +1,128 @@ +import { + createMultiMap, + emptyArray, + GetCanonicalFileName, + MultiMap, + startsWith, +} from "./core"; +import * as Debug from "./debug"; +import { ModeAwareCache } from "./moduleNameResolver"; +import { + ensureTrailingDirectorySeparator, + getNormalizedAbsolutePath, + getPathComponents, + getPathFromPathComponents, + toPath, +} from "./path"; +import { containsIgnoredPath } from "./sysUtilities"; +import { + Path, + ResolvedModuleFull, + ResolvedTypeReferenceDirective, + ResolvedTypeReferenceDirectiveWithFailedLookupLocations, + SourceFile, +} from "./types"; + +/** @internal */ +export interface SymlinkedDirectory { + /** Matches the casing returned by `realpath`. Used to compute the `realpath` of children. */ + real: string; + /** toPath(real). Stored to avoid repeated recomputation. */ + realPath: Path; +} + +/** @internal */ +export interface SymlinkCache { + /** Gets a map from symlink to realpath. Keys have trailing directory separators. */ + getSymlinkedDirectories(): ReadonlyMap | undefined; + /** Gets a map from realpath to symlinks. Keys have trailing directory separators. */ + getSymlinkedDirectoriesByRealpath(): MultiMap | undefined; + /** Gets a map from symlink to realpath */ + getSymlinkedFiles(): ReadonlyMap | undefined; + setSymlinkedDirectory(symlink: string, real: SymlinkedDirectory | false): void; + setSymlinkedFile(symlinkPath: Path, real: string): void; + /** + * @internal + * Uses resolvedTypeReferenceDirectives from program instead of from files, since files + * don't include automatic type reference directives. Must be called only when + * `hasProcessedResolutions` returns false (once per cache instance). + */ + setSymlinksFromResolutions(files: readonly SourceFile[], typeReferenceDirectives: ModeAwareCache): void; + /** + * @internal + * Whether `setSymlinksFromResolutions` has already been called. + */ + hasProcessedResolutions(): boolean; +} + +/** @internal */ +export function createSymlinkCache(cwd: string, getCanonicalFileName: GetCanonicalFileName): SymlinkCache { + let symlinkedDirectories: Map | undefined; + let symlinkedDirectoriesByRealpath: MultiMap | undefined; + let symlinkedFiles: Map | undefined; + let hasProcessedResolutions = false; + return { + getSymlinkedFiles: () => symlinkedFiles, + getSymlinkedDirectories: () => symlinkedDirectories, + getSymlinkedDirectoriesByRealpath: () => symlinkedDirectoriesByRealpath, + setSymlinkedFile: (path, real) => (symlinkedFiles || (symlinkedFiles = new Map())).set(path, real), + setSymlinkedDirectory: (symlink, real) => { + // Large, interconnected dependency graphs in pnpm will have a huge number of symlinks + // where both the realpath and the symlink path are inside node_modules/.pnpm. Since + // this path is never a candidate for a module specifier, we can ignore it entirely. + let symlinkPath = toPath(symlink, cwd, getCanonicalFileName); + if (!containsIgnoredPath(symlinkPath)) { + symlinkPath = ensureTrailingDirectorySeparator(symlinkPath); + if (real !== false && !symlinkedDirectories?.has(symlinkPath)) { + (symlinkedDirectoriesByRealpath ||= createMultiMap()).add(ensureTrailingDirectorySeparator(real.realPath), symlink); + } + (symlinkedDirectories || (symlinkedDirectories = new Map())).set(symlinkPath, real); + } + }, + setSymlinksFromResolutions(files, typeReferenceDirectives) { + Debug.assert(!hasProcessedResolutions); + hasProcessedResolutions = true; + for (const file of files) { + file.resolvedModules?.forEach(resolution => processResolution(this, resolution.resolvedModule)); + file.resolvedTypeReferenceDirectiveNames?.forEach(resolution => processResolution(this, resolution.resolvedTypeReferenceDirective)); + } + typeReferenceDirectives.forEach(resolution => processResolution(this, resolution.resolvedTypeReferenceDirective)); + }, + hasProcessedResolutions: () => hasProcessedResolutions, + }; + + function processResolution(cache: SymlinkCache, resolution: ResolvedModuleFull | ResolvedTypeReferenceDirective | undefined) { + if (!resolution || !resolution.originalPath || !resolution.resolvedFileName) return; + const { resolvedFileName, originalPath } = resolution; + cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedFileName); + const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedFileName, originalPath, cwd, getCanonicalFileName) || emptyArray; + if (commonResolved && commonOriginal) { + cache.setSymlinkedDirectory( + commonOriginal, + { real: commonResolved, realPath: toPath(commonResolved, cwd, getCanonicalFileName) }); + } + } +} + +function guessDirectorySymlink(a: string, b: string, cwd: string, getCanonicalFileName: GetCanonicalFileName): [string, string] | undefined { + const aParts = getPathComponents(getNormalizedAbsolutePath(a, cwd)); + const bParts = getPathComponents(getNormalizedAbsolutePath(b, cwd)); + let isDirectory = false; + while ( + aParts.length >= 2 && bParts.length >= 2 && + !isNodeModulesOrScopedPackageDirectory(aParts[aParts.length - 2], getCanonicalFileName) && + !isNodeModulesOrScopedPackageDirectory(bParts[bParts.length - 2], getCanonicalFileName) && + getCanonicalFileName(aParts[aParts.length - 1]) === getCanonicalFileName(bParts[bParts.length - 1]) + ) { + aParts.pop(); + bParts.pop(); + isDirectory = true; + } + return isDirectory ? [getPathFromPathComponents(aParts), getPathFromPathComponents(bParts)] : undefined; +} + +// KLUDGE: Don't assume one 'node_modules' links to another. More likely a single directory inside the node_modules is the symlink. +// ALso, don't assume that an `@foo` directory is linked. More likely the contents of that are linked. +function isNodeModulesOrScopedPackageDirectory(s: string | undefined, getCanonicalFileName: GetCanonicalFileName): boolean { + return s !== undefined && (getCanonicalFileName(s) === "node_modules" || startsWith(s, "@")); +} diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index ace8ed10678dc..3eda04396a8b5 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -1,52 +1,66 @@ +import { matchesExclude } from "./commandLineParser"; import { - AssertionLevel, - closeFileWatcher, - closeFileWatcherOf, - combinePaths, - Comparison, contains, - containsPath, createGetCanonicalFileName, createMultiMap, - Debug, - directorySeparator, emptyArray, - emptyFileSystemEntries, endsWith, enumerateInsertsAndDeletes, - FileSystemEntries, - getDirectoryPath, - getFallbackOptions, - getNormalizedAbsolutePath, - getRelativePathToDirectoryOrUrl, - getRootLength, getStringComparer, isArray, - isNodeLikeSystem, isString, mapDefined, - matchesExclude, - matchFiles, memoize, - ModuleImportResult, noop, - normalizePath, - normalizeSlashes, orderedRemoveItem, - Path, - perfLogger, - PollingWatchKind, - resolveJSModule, some, startsWith, stringContains, - timestamp, unorderedRemoveItem, +} from "./core"; +import { Comparison } from "./corePublic"; +import * as Debug from "./debug"; +import { + emptyFileSystemEntries, + FileSystemEntries, + matchFiles, +} from "./fileMatcher"; +import { resolveJSModule } from "./moduleNameResolver"; +import { + combinePaths, + containsPath, + directorySeparator, + getDirectoryPath, + getNormalizedAbsolutePath, + getRelativePathToDirectoryOrUrl, + getRootLength, + normalizePath, + normalizeSlashes, +} from "./path"; +import { perfLogger } from "./perfLogger"; +import { timestamp } from "./performanceCore"; +import { isNodeLikeSystem } from "./platform"; +import { ignoredPaths } from "./sysUtilities"; +import { + DirectoryWatcherCallback, + FileWatcher, + FileWatcherCallback, + FileWatcherEventKind, + Path, + PollingWatchKind, + System, WatchDirectoryKind, WatchFileKind, WatchOptions, +} from "./types"; +import { + closeFileWatcher, writeFileEnsuringDirectories, -} from "./_namespaces/ts"; +} from "./utilities"; +import { + closeFileWatcherOf, + getFallbackOptions, +} from "./watchUtilities"; declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any; declare function clearTimeout(handle: any): void; @@ -78,14 +92,6 @@ export function setStackTraceLimit() { } } -export enum FileWatcherEventKind { - Created, - Changed, - Deleted -} - -export type FileWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, modifiedTime?: Date) => void; -export type DirectoryWatcherCallback = (fileName: string) => void; interface WatchedFile { readonly fileName: string; readonly callback: FileWatcherCallback; @@ -534,9 +540,6 @@ export function getFileWatcherEventKind(oldTime: number, newTime: number) { : FileWatcherEventKind.Changed; } -/** @internal */ -export const ignoredPaths = ["/node_modules/.", "/.git", "/.#"]; - let curSysLog: (s: string) => void = noop; /** @internal */ @@ -1296,149 +1299,6 @@ export function patchWriteFileEnsuringDirectory(sys: System) { path => sys.directoryExists(path)); } -export type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex"; - -/** @internal */ -export interface NodeBuffer extends Uint8Array { - constructor: any; - write(str: string, encoding?: BufferEncoding): number; - write(str: string, offset: number, encoding?: BufferEncoding): number; - write(str: string, offset: number, length: number, encoding?: BufferEncoding): number; - toString(encoding?: string, start?: number, end?: number): string; - toJSON(): { type: "Buffer"; data: number[] }; - equals(otherBuffer: Uint8Array): boolean; - compare( - otherBuffer: Uint8Array, - targetStart?: number, - targetEnd?: number, - sourceStart?: number, - sourceEnd?: number - ): number; - copy(targetBuffer: Uint8Array, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; - slice(begin?: number, end?: number): Buffer; - subarray(begin?: number, end?: number): Buffer; - writeUIntLE(value: number, offset: number, byteLength: number): number; - writeUIntBE(value: number, offset: number, byteLength: number): number; - writeIntLE(value: number, offset: number, byteLength: number): number; - writeIntBE(value: number, offset: number, byteLength: number): number; - readUIntLE(offset: number, byteLength: number): number; - readUIntBE(offset: number, byteLength: number): number; - readIntLE(offset: number, byteLength: number): number; - readIntBE(offset: number, byteLength: number): number; - readUInt8(offset: number): number; - readUInt16LE(offset: number): number; - readUInt16BE(offset: number): number; - readUInt32LE(offset: number): number; - readUInt32BE(offset: number): number; - readInt8(offset: number): number; - readInt16LE(offset: number): number; - readInt16BE(offset: number): number; - readInt32LE(offset: number): number; - readInt32BE(offset: number): number; - readFloatLE(offset: number): number; - readFloatBE(offset: number): number; - readDoubleLE(offset: number): number; - readDoubleBE(offset: number): number; - reverse(): this; - swap16(): Buffer; - swap32(): Buffer; - swap64(): Buffer; - writeUInt8(value: number, offset: number): number; - writeUInt16LE(value: number, offset: number): number; - writeUInt16BE(value: number, offset: number): number; - writeUInt32LE(value: number, offset: number): number; - writeUInt32BE(value: number, offset: number): number; - writeInt8(value: number, offset: number): number; - writeInt16LE(value: number, offset: number): number; - writeInt16BE(value: number, offset: number): number; - writeInt32LE(value: number, offset: number): number; - writeInt32BE(value: number, offset: number): number; - writeFloatLE(value: number, offset: number): number; - writeFloatBE(value: number, offset: number): number; - writeDoubleLE(value: number, offset: number): number; - writeDoubleBE(value: number, offset: number): number; - readBigUInt64BE?(offset?: number): bigint; - readBigUInt64LE?(offset?: number): bigint; - readBigInt64BE?(offset?: number): bigint; - readBigInt64LE?(offset?: number): bigint; - writeBigInt64BE?(value: bigint, offset?: number): number; - writeBigInt64LE?(value: bigint, offset?: number): number; - writeBigUInt64BE?(value: bigint, offset?: number): number; - writeBigUInt64LE?(value: bigint, offset?: number): number; - fill(value: string | Uint8Array | number, offset?: number, end?: number, encoding?: BufferEncoding): this; - indexOf(value: string | number | Uint8Array, byteOffset?: number, encoding?: BufferEncoding): number; - lastIndexOf(value: string | number | Uint8Array, byteOffset?: number, encoding?: BufferEncoding): number; - entries(): IterableIterator<[number, number]>; - includes(value: string | number | Buffer, byteOffset?: number, encoding?: BufferEncoding): boolean; - keys(): IterableIterator; - values(): IterableIterator; -} - -/** @internal */ -export interface Buffer extends NodeBuffer { } - -// TODO: GH#18217 Methods on System are often used as if they are certainly defined -export interface System { - args: string[]; - newLine: string; - useCaseSensitiveFileNames: boolean; - write(s: string): void; - writeOutputIsTTY?(): boolean; - getWidthOfTerminal?(): number; - readFile(path: string, encoding?: string): string | undefined; - getFileSize?(path: string): number; - writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; - - /** - * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that - * use native OS file watching - */ - watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher; - watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher; - resolvePath(path: string): string; - fileExists(path: string): boolean; - directoryExists(path: string): boolean; - createDirectory(path: string): void; - getExecutingFilePath(): string; - getCurrentDirectory(): string; - getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[]; - getModifiedTime?(path: string): Date | undefined; - setModifiedTime?(path: string, time: Date): void; - deleteFile?(path: string): void; - /** - * A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm) - */ - createHash?(data: string): string; - /** This must be cryptographically secure. Only implement this method using `crypto.createHash("sha256")`. */ - createSHA256Hash?(data: string): string; - getMemoryUsage?(): number; - exit(exitCode?: number): void; - /** @internal */ enableCPUProfiler?(path: string, continuation: () => void): boolean; - /** @internal */ disableCPUProfiler?(continuation: () => void): boolean; - /** @internal */ cpuProfilingEnabled?(): boolean; - realpath?(path: string): string; - /** @internal */ getEnvironmentVariable(name: string): string; - /** @internal */ tryEnableSourceMapsForHost?(): void; - /** @internal */ debugMode?: boolean; - setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; - clearTimeout?(timeoutId: any): void; - clearScreen?(): void; - /** @internal */ setBlocking?(): void; - base64decode?(input: string): string; - base64encode?(input: string): string; - /** @internal */ bufferFrom?(input: string, encoding?: string): Buffer; - /** @internal */ require?(baseDir: string, moduleName: string): ModuleImportResult; - - // For testing - /** @internal */ now?(): Date; - /** @internal */ storeFilesChangingSignatureDuringEmit?: boolean; -} - -export interface FileWatcher { - close(): void; -} - interface DirectoryWatcher extends FileWatcher { referenceCount: number; } @@ -2003,10 +1863,9 @@ export function setSys(s: System) { if (sys && sys.getEnvironmentVariable) { setCustomPollingValues(sys); - Debug.setAssertionLevel(/^development$/i.test(sys.getEnvironmentVariable("NODE_ENV")) - ? AssertionLevel.Normal - : AssertionLevel.None); + Debug.setAssertionLevel(/^development$/i.test(sys.getEnvironmentVariable("NODE_ENV")) ? Debug.AssertionLevel.Normal : Debug.AssertionLevel.None); } + if (sys && sys.debugMode) { - Debug.isDebugging = true; + Debug.setIsDebugging(true); } diff --git a/src/compiler/sysUtilities.ts b/src/compiler/sysUtilities.ts new file mode 100644 index 0000000000000..0cc707e930bb6 --- /dev/null +++ b/src/compiler/sysUtilities.ts @@ -0,0 +1,9 @@ +import { some, stringContains } from "./core"; + +/** @internal */ +export const ignoredPaths = ["/node_modules/.", "/.git", "/.#"]; + +/** @internal */ +export function containsIgnoredPath(path: string) { + return some(ignoredPaths, p => stringContains(path, p)); +} diff --git a/src/compiler/tracing.ts b/src/compiler/tracing.ts index 4f091195827a8..c738c225a2c35 100644 --- a/src/compiler/tracing.ts +++ b/src/compiler/tracing.ts @@ -1,10 +1,11 @@ +import * as Debug from "./debug"; +import { combinePaths } from "./path"; +import * as performance from "./performance"; +import { timestamp } from "./performanceCore"; +import { getLineAndCharacterOfPosition } from "./scanner"; import { - combinePaths, ConditionalType, - Debug, EvolvingArrayType, - getLineAndCharacterOfPosition, - getSourceFileOfNode, IndexedAccessType, IndexType, IntersectionType, @@ -14,14 +15,13 @@ import { Path, ReverseMappedType, SubstitutionType, - timestamp, Type, TypeFlags, TypeReference, - unescapeLeadingUnderscores, UnionType, -} from "./_namespaces/ts"; -import * as performance from "./_namespaces/ts.performance"; +} from "./types"; +import { getSourceFileOfNode } from "./utilities"; +import { unescapeLeadingUnderscores } from "./utilitiesPublic"; /* Tracing events for the compiler. */ diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index a6bf29119479c..b12acb97b705c 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -1,16 +1,55 @@ import { addRange, append, + emptyArray, + map, + memoize, + noop, + notImplemented, + returnUndefined, + some, +} from "./core"; +import * as Debug from "./debug"; +import { createEmitHelperFactory } from "./factory/emitHelpers"; +import { + disposeEmitNodes, + setEmitFlags, +} from "./factory/emitNode"; +import { factory } from "./factory/nodeFactory"; +import { + isBundle, + isSourceFile, +} from "./factory/nodeTests"; +import * as performance from "./performance"; +import { tracing } from "./tracing"; +import { transformClassFields } from "./transformers/classFields"; +import { transformDeclarations } from "./transformers/declarations"; +import { transformES5 } from "./transformers/es5"; +import { transformES2015 } from "./transformers/es2015"; +import { transformES2016 } from "./transformers/es2016"; +import { transformES2017 } from "./transformers/es2017"; +import { transformES2018 } from "./transformers/es2018"; +import { transformES2019 } from "./transformers/es2019"; +import { transformES2020 } from "./transformers/es2020"; +import { transformES2021 } from "./transformers/es2021"; +import { transformESDecorators } from "./transformers/esDecorators"; +import { transformESNext } from "./transformers/esnext"; +import { transformGenerators } from "./transformers/generators"; +import { transformJsx } from "./transformers/jsx"; +import { transformLegacyDecorators } from "./transformers/legacyDecorators"; +import { transformECMAScriptModule } from "./transformers/module/esnextAnd2015"; +import { transformModule } from "./transformers/module/module"; +import { transformNodeModule } from "./transformers/module/node"; +import { transformSystemModule } from "./transformers/module/system"; +import { transformTypeScript } from "./transformers/ts"; +import { chainBundle } from "./transformers/utilities"; +import { Bundle, - chainBundle, CompilerOptions, - createEmitHelperFactory, CustomTransformer, CustomTransformerFactory, CustomTransformers, - Debug, DiagnosticWithLocation, - disposeEmitNodes, EmitFlags, EmitHelper, EmitHint, @@ -18,63 +57,32 @@ import { EmitOnly, EmitResolver, EmitTransformers, - emptyArray, - factory, FunctionDeclaration, - getEmitFlags, - getEmitModuleKind, - getEmitScriptTarget, - getJSXTransformEnabled, - getParseTreeNode, - getSourceFileOfNode, - getUseDefineForClassFields, Identifier, - isBundle, - isSourceFile, LexicalEnvironmentFlags, - map, - memoize, ModuleKind, Node, NodeFactory, NodeFlags, - noop, - notImplemented, - returnUndefined, ScriptTarget, - setEmitFlags, - some, SourceFile, Statement, SyntaxKind, - tracing, TransformationContext, TransformationResult, - transformClassFields, - transformDeclarations, - transformECMAScriptModule, Transformer, TransformerFactory, - transformES5, - transformES2015, - transformES2016, - transformES2017, - transformES2018, - transformES2019, - transformES2020, - transformES2021, - transformESDecorators, - transformESNext, - transformGenerators, - transformJsx, - transformLegacyDecorators, - transformModule, - transformNodeModule, - transformSystemModule, - transformTypeScript, VariableDeclaration, -} from "./_namespaces/ts"; -import * as performance from "./_namespaces/ts.performance"; +} from "./types"; +import { + getEmitFlags, + getEmitModuleKind, + getEmitScriptTarget, + getJSXTransformEnabled, + getSourceFileOfNode, + getUseDefineForClassFields, +} from "./utilities"; +import { getParseTreeNode } from "./utilitiesPublic"; function getModuleTransformer(moduleKind: ModuleKind): TransformerFactory { switch (moduleKind) { diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 0faf191377a4a..0da080c1dcee8 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -1,160 +1,119 @@ import { - __String, - AccessorDeclaration, - accessPrivateIdentifier as accessPrivateIdentifierCommon, - addEmitFlags, - addEmitHelpers, addRange, - AnonymousFunctionDefinition, append, - ArrayAssignmentElement, - AssignmentExpression, - AssignmentOperator, - AssignmentPattern, - AutoAccessorPropertyDeclaration, - BinaryExpression, - BindingElement, - Bundle, - CallExpression, - chainBundle, - ClassDeclaration, - ClassElement, - ClassExpression, - ClassLikeDeclaration, - classOrConstructorParameterIsDecorated, - ClassStaticBlockDeclaration, - CommaListExpression, compact, - ComputedPropertyName, - ConstructorDeclaration, - createAccessorPropertyBackingField, - createAccessorPropertyGetRedirector, - createAccessorPropertySetRedirector, - createMemberAccessForPropertyName, - Debug, - ElementAccessExpression, - EmitFlags, - EmitHint, - EqualsToken, - expandPreOrPostfixIncrementOrDecrementExpression, - ExportAssignment, - Expression, - ExpressionStatement, - ExpressionWithTypeArguments, filter, find, - findComputedPropertyNameCacheAssignment, - findSuperStatementIndex, - flattenCommaList, - ForStatement, - GeneratedIdentifier, - GeneratedIdentifierFlags, - GeneratedNamePart, - GetAccessorDeclaration, + map, + some, + tryCast, +} from "../core"; +import * as Debug from "../debug"; +import { isCallToHelper } from "../factory/emitHelpers"; +import { + addEmitFlags, + addEmitHelpers, getCommentRange, - getEffectiveBaseTypeNode, - getEmitFlags, - getEmitScriptTarget, - getInternalEmitFlags, - getNameOfDeclaration, - getNonAssignmentOperatorForCompoundAssignment, - getOriginalNode, - getOriginalNodeId, - getPrivateIdentifier, - getProperties, getSourceMapRange, - getStaticPropertiesAndClassStaticBlock, - getUseDefineForClassFields, - hasAbstractModifier, - hasAccessorModifier, - hasDecorators, - hasStaticModifier, - hasSyntacticModifier, - Identifier, - InKeyword, - InternalEmitFlags, + removeAllComments, + setCommentRange, + setEmitFlags, + setSourceMapRange, + setSyntheticLeadingComments, + setSyntheticTrailingComments, +} from "../factory/emitNode"; +import { isAccessorModifier, - isArrayBindingOrAssignmentElement, isArrayLiteralExpression, isArrowFunction, - isAssignmentExpression, - isAutoAccessorPropertyDeclaration, - isBindingName, - isCallChain, - isCallToHelper, isClassDeclaration, - isClassElement, isClassExpression, - isClassLike, isClassStaticBlockDeclaration, - isCommaExpression, - isCompoundAssignment, isComputedPropertyName, isConstructorDeclaration, - isDestructuringAssignment, isElementAccessExpression, - isExportOrDefaultModifier, - isExpression, isExpressionStatement, - isForInitializer, - isGeneratedIdentifier, - isGeneratedPrivateIdentifier, - isGetAccessor, isGetAccessorDeclaration, isHeritageClause, isIdentifier, - isInitializedProperty, - isLeftHandSideExpression, isMethodDeclaration, - isModifier, - isModifierLike, - isNamedEvaluation, - isNonStaticMethodOrAccessorWithPrivateName, isNumericLiteral, - isObjectBindingOrAssignmentElement, - isObjectLiteralElementLike, isObjectLiteralExpression, isOmittedExpression, isParameter, - isParameterPropertyDeclaration, isParenthesizedExpression, isPrefixUnaryExpression, isPrivateIdentifier, - isPrivateIdentifierClassElementDeclaration, - isPrivateIdentifierPropertyAccessExpression, isPropertyAccessExpression, isPropertyAssignment, isPropertyDeclaration, - isPropertyName, - isPropertyNameLiteral, - isSetAccessor, isSetAccessorDeclaration, isShorthandPropertyAssignment, - isSimpleCopiableExpression, - isSimpleInlineableExpression, isSpreadAssignment, isSpreadElement, - isStatement, - isStatic, isStaticModifier, - isSuperProperty, - isTemplateLiteral, - isThisProperty, isVoidExpression, +} from "../factory/nodeTests"; +import { + createAccessorPropertyBackingField, + createAccessorPropertyGetRedirector, + createAccessorPropertySetRedirector, + createMemberAccessForPropertyName, + expandPreOrPostfixIncrementOrDecrementExpression, + findComputedPropertyNameCacheAssignment, + flattenCommaList, + isCommaExpression, + isExportOrDefaultModifier, + startOnNewLine, +} from "../factory/utilities"; +import { + setOriginalNode, + setTextRange, +} from "../factory/utilitiesPublic"; +import { + __String, + AccessorDeclaration, + ArrayAssignmentElement, + AssignmentExpression, + AssignmentOperator, + AssignmentPattern, + AutoAccessorPropertyDeclaration, + BinaryExpression, + BindingElement, + Bundle, + CallExpression, + ClassDeclaration, + ClassElement, + ClassExpression, + ClassLikeDeclaration, + ClassStaticBlockDeclaration, + CommaListExpression, + ComputedPropertyName, + ConstructorDeclaration, + ElementAccessExpression, + EmitFlags, + EmitHint, + EqualsToken, + ExportAssignment, + Expression, + ExpressionStatement, + ExpressionWithTypeArguments, + ForStatement, + GeneratedIdentifier, + GeneratedIdentifierFlags, + GeneratedNamePart, + GetAccessorDeclaration, + Identifier, + InKeyword, + InternalEmitFlags, LeftHandSideExpression, - LexicalEnvironment, - map, MethodDeclaration, Modifier, ModifierFlags, ModifierLike, - moveRangePastModifiers, - moveRangePos, - newPrivateEnvironment, Node, NodeCheckFlags, NodeFactory, - nodeIsSynthesized, ObjectLiteralElement, OmittedExpression, OuterExpressionKinds, @@ -163,7 +122,6 @@ import { PartiallyEmittedExpression, PostfixUnaryExpression, PrefixUnaryExpression, - PrivateEnvironment, PrivateIdentifier, PrivateIdentifierAccessorDeclaration, PrivateIdentifierKind, @@ -174,26 +132,12 @@ import { PropertyAssignment, PropertyDeclaration, PropertyName, - removeAllComments, ScriptTarget, SetAccessorDeclaration, - setCommentRange, - setEmitFlags, - setOriginalNode, - setPrivateIdentifier, - setSourceMapRange, - setSyntheticLeadingComments, - setSyntheticTrailingComments, - setTextRange, ShorthandPropertyAssignment, - skipOuterExpressions, - skipParentheses, - skipPartiallyEmittedExpressions, - some, SourceFile, SpreadAssignment, SpreadElement, - startOnNewLine, Statement, SuperProperty, SyntaxKind, @@ -202,11 +146,68 @@ import { ThisExpression, TransformationContext, TransformFlags, - tryCast, - tryGetTextOfPropertyName, - unescapeLeadingUnderscores, VariableDeclaration, VariableStatement, + Visitor, + VisitResult, +} from "../types"; +import { + AnonymousFunctionDefinition, + classOrConstructorParameterIsDecorated, + getEffectiveBaseTypeNode, + getEmitFlags, + getEmitScriptTarget, + getInternalEmitFlags, + getUseDefineForClassFields, + hasAbstractModifier, + hasAccessorModifier, + hasDecorators, + hasStaticModifier, + hasSyntacticModifier, + isAssignmentExpression, + isDestructuringAssignment, + isNamedEvaluation, + isPropertyNameLiteral, + isStatic, + isSuperProperty, + isThisProperty, + moveRangePastModifiers, + moveRangePos, + nodeIsSynthesized, + skipOuterExpressions, + skipParentheses, + tryGetTextOfPropertyName, +} from "../utilities"; +import { + getNameOfDeclaration, + getOriginalNode, + isArrayBindingOrAssignmentElement, + isAutoAccessorPropertyDeclaration, + isBindingName, + isCallChain, + isClassElement, + isClassLike, + isExpression, + isForInitializer, + isGeneratedIdentifier, + isGeneratedPrivateIdentifier, + isGetAccessor, + isLeftHandSideExpression, + isModifier, + isModifierLike, + isObjectBindingOrAssignmentElement, + isObjectLiteralElementLike, + isParameterPropertyDeclaration, + isPrivateIdentifierClassElementDeclaration, + isPrivateIdentifierPropertyAccessExpression, + isPropertyName, + isSetAccessor, + isStatement, + isTemplateLiteral, + skipPartiallyEmittedExpressions, + unescapeLeadingUnderscores, +} from "../utilitiesPublic"; +import { visitArray, visitCommaListElements, visitEachChild, @@ -214,10 +215,27 @@ import { visitIterationBody, visitNode, visitNodes, - Visitor, visitParameterList, - VisitResult, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { + accessPrivateIdentifier as accessPrivateIdentifierCommon, + chainBundle, + findSuperStatementIndex, + getNonAssignmentOperatorForCompoundAssignment, + getOriginalNodeId, + getPrivateIdentifier, + getProperties, + getStaticPropertiesAndClassStaticBlock, + isCompoundAssignment, + isInitializedProperty, + isNonStaticMethodOrAccessorWithPrivateName, + isSimpleCopiableExpression, + isSimpleInlineableExpression, + LexicalEnvironment, + newPrivateEnvironment, + PrivateEnvironment, + setPrivateIdentifier, +} from "./utilities"; const enum ClassPropertySubstitutionFlags { /** diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 4be05fd79eb06..b37d18a3d5697 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -1,135 +1,59 @@ import { - AccessorDeclaration, - addRelatedInfo, - AllAccessorDeclarations, - AnyImportSyntax, append, - ArrayBindingElement, arrayFrom, - AssertClause, - BindingElement, - BindingName, - BindingPattern, - Bundle, - CallSignatureDeclaration, - canHaveModifiers, - canProduceDiagnostics, - ClassDeclaration, - CommentRange, compact, concatenate, - ConditionalTypeNode, - ConstructorDeclaration, - ConstructorTypeNode, - ConstructSignatureDeclaration, contains, - createDiagnosticForNode, - createEmptyExports, - createGetSymbolAccessibilityDiagnosticForNode, - createGetSymbolAccessibilityDiagnosticForNodeName, - createSymbolTable, - createUnparsedSourceFile, - Debug, - Declaration, - DeclarationDiagnosticProducing, - DeclarationName, - declarationNameToString, - Diagnostics, - DiagnosticWithLocation, - EmitFlags, - EmitHost, - EmitResolver, emptyArray, - EntityNameOrEntityNameExpression, - EnumDeclaration, - ExportAssignment, - ExportDeclaration, - ExpressionWithTypeArguments, - factory, - FileReference, filter, flatMap, flatten, forEach, - FunctionDeclaration, - FunctionTypeNode, - GeneratedIdentifierFlags, - GetAccessorDeclaration, - getCommentRange, - getDirectoryPath, - getEffectiveBaseTypeNode, - getEffectiveModifierFlags, - getExternalModuleImportEqualsDeclarationExpression, - getExternalModuleNameFromDeclaration, - getFirstConstructorWithBody, - getLeadingCommentRanges, - getLeadingCommentRangesOfNode, - getLineAndCharacterOfPosition, - getNameOfDeclaration, - getOriginalNodeId, - getOutputPathsFor, - getParseTreeNode, - getRelativePathToDirectoryOrUrl, - getResolutionModeOverrideForClause, - getResolvedExternalModuleName, - getSetAccessorValueParameter, - getSourceFileOfNode, - GetSymbolAccessibilityDiagnostic, - getTextOfNode, - getThisParameter, - getTrailingCommentRanges, - hasDynamicName, - hasEffectiveModifier, - hasExtension, - hasJSDocNodes, - HasModifiers, - hasSyntacticModifier, - HeritageClause, - Identifier, - ImportDeclaration, - ImportEqualsDeclaration, - ImportTypeNode, - IndexSignatureDeclaration, - InterfaceDeclaration, - isAnyImportSyntax, isArray, - isArrayBindingElement, + last, + length, + map, + mapDefined, + orderedRemoveItem, + pushIfUnique, + some, + startsWith, + stringContains, + toFileNameLowerCase, + tryCast, +} from "../core"; +import * as Debug from "../debug"; +import { Diagnostics } from "../diagnosticInformationMap.generated"; +import { getOutputPathsFor } from "../emitter"; +import { + getCommentRange, + removeAllComments, + setCommentRange, + setEmitFlags, +} from "../factory/emitNode"; +import { + createUnparsedSourceFile, + factory, +} from "../factory/nodeFactory"; +import { isBinaryExpression, isBindingElement, - isBindingPattern, isClassDeclaration, - isClassElement, - isDeclaration, isElementAccessExpression, - isEntityName, - isEntityNameExpression, isExportAssignment, isExportDeclaration, isExpressionWithTypeArguments, - isExternalModule, - isExternalModuleAugmentation, - isExternalModuleIndicator, isExternalModuleReference, - isExternalOrCommonJsModule, isFunctionDeclaration, - isFunctionLike, - isGlobalScopeAugmentation, isIdentifier, - isIdentifierANonContextualKeyword, - isIdentifierText, isImportDeclaration, isImportEqualsDeclaration, isIndexSignatureDeclaration, isInterfaceDeclaration, - isJsonSourceFile, - isLateVisibilityPaintedStatement, - isLiteralImportTypeNode, isMappedTypeNode, isMethodDeclaration, isMethodSignature, - isModifier, isModuleDeclaration, - isNightly, isOmittedExpression, isPrivateIdentifier, isPropertyAccessExpression, @@ -137,26 +61,88 @@ import { isSemicolonClassElement, isSetAccessorDeclaration, isSourceFile, - isSourceFileJS, - isSourceFileNotJson, - isStatement, - isStringANonContextualKeyword, isStringLiteral, - isStringLiteralLike, isTupleTypeNode, isTypeAliasDeclaration, - isTypeElement, - isTypeNode, isTypeParameterDeclaration, isTypeQueryNode, isUnparsedSource, isVariableDeclaration, - last, +} from "../factory/nodeTests"; +import { createEmptyExports } from "../factory/utilities"; +import { + canHaveModifiers, + setOriginalNode, + setTextRange, +} from "../factory/utilitiesPublic"; +import { pathContainsNodeModules } from "../moduleNameResolver"; +import { + getExternalModuleNameFromDeclaration, + getResolvedExternalModuleName, +} from "../moduleNameResolverUtilities"; +import { getModuleSpecifier } from "../moduleSpecifiers"; +import { parseNodeFactory } from "../parser"; +import { setParent } from "../parserUtilities"; +import { + getDirectoryPath, + getRelativePathToDirectoryOrUrl, + hasExtension, + normalizeSlashes, + pathIsRelative, + toPath, +} from "../path"; +import { getResolutionModeOverrideForClause } from "../program"; +import { + getLeadingCommentRanges, + getLineAndCharacterOfPosition, + getTrailingCommentRanges, + isIdentifierText, + skipTrivia, +} from "../scanner"; +import { transformNodes } from "../transformer"; +import { + AccessorDeclaration, + AllAccessorDeclarations, + AnyImportSyntax, + ArrayBindingElement, + AssertClause, + BindingElement, + BindingName, + BindingPattern, + Bundle, + CallSignatureDeclaration, + ClassDeclaration, + CommentRange, + ConditionalTypeNode, + ConstructorDeclaration, + ConstructorTypeNode, + ConstructSignatureDeclaration, + Declaration, + DeclarationName, + DiagnosticWithLocation, + EmitFlags, + EmitHost, + EmitResolver, + EntityNameOrEntityNameExpression, + EnumDeclaration, + ExportAssignment, + ExportDeclaration, + ExpressionWithTypeArguments, + FileReference, + FunctionDeclaration, + FunctionTypeNode, + GeneratedIdentifierFlags, + GetAccessorDeclaration, + HasModifiers, + HeritageClause, + Identifier, + ImportDeclaration, + ImportEqualsDeclaration, + ImportTypeNode, + IndexSignatureDeclaration, + InterfaceDeclaration, LateBoundDeclaration, LateVisibilityPaintedStatement, - length, - map, - mapDefined, MethodDeclaration, MethodSignature, Modifier, @@ -165,39 +151,22 @@ import { ModuleDeclaration, NamedDeclaration, NamespaceDeclaration, - needsScopeMarker, Node, NodeArray, NodeBuilderFlags, NodeFactory, NodeFlags, NodeId, - normalizeSlashes, OmittedExpression, - orderedRemoveItem, ParameterDeclaration, - parseNodeFactory, - pathContainsNodeModules, - pathIsRelative, PropertyDeclaration, PropertySignature, - pushIfUnique, - removeAllComments, ResolutionMode, ScriptTarget, SetAccessorDeclaration, - setCommentRange, - setEmitFlags, - setOriginalNode, - setParent, - setTextRange, SignatureDeclaration, - skipTrivia, - some, SourceFile, - startsWith, Statement, - stringContains, StringLiteral, Symbol, SymbolAccessibility, @@ -205,26 +174,81 @@ import { SymbolFlags, SymbolTracker, SyntaxKind, - toFileNameLowerCase, - toPath, TransformationContext, - transformNodes, - tryCast, TypeAliasDeclaration, TypeNode, TypeParameterDeclaration, TypeReferenceNode, - unescapeLeadingUnderscores, UnparsedSource, VariableDeclaration, VariableStatement, + VisitResult, +} from "../types"; +import { + addRelatedInfo, + createDiagnosticForNode, + createSymbolTable, + declarationNameToString, + getEffectiveBaseTypeNode, + getEffectiveModifierFlags, + getExternalModuleImportEqualsDeclarationExpression, + getFirstConstructorWithBody, + getLeadingCommentRangesOfNode, + getSetAccessorValueParameter, + getSourceFileOfNode, + getTextOfNode, + getThisParameter, + hasDynamicName, + hasEffectiveModifier, + hasSyntacticModifier, + isAnyImportSyntax, + isEntityNameExpression, + isExternalModule, + isExternalModuleAugmentation, + isExternalOrCommonJsModule, + isGlobalScopeAugmentation, + isIdentifierANonContextualKeyword, + isJsonSourceFile, + isLateVisibilityPaintedStatement, + isLiteralImportTypeNode, + isNightly, + isSourceFileJS, + isSourceFileNotJson, + isStringANonContextualKeyword, +} from "../utilities"; +import { + getNameOfDeclaration, + getParseTreeNode, + hasJSDocNodes, + isArrayBindingElement, + isBindingPattern, + isClassElement, + isDeclaration, + isEntityName, + isExternalModuleIndicator, + isFunctionLike, + isModifier, + isStatement, + isStringLiteralLike, + isTypeElement, + isTypeNode, + needsScopeMarker, + unescapeLeadingUnderscores, +} from "../utilitiesPublic"; +import { visitArray, visitEachChild, visitNode, visitNodes, - VisitResult, -} from "../_namespaces/ts"; -import * as moduleSpecifiers from "../_namespaces/ts.moduleSpecifiers"; +} from "../visitorPublic"; +import { + canProduceDiagnostics, + createGetSymbolAccessibilityDiagnosticForNode, + createGetSymbolAccessibilityDiagnosticForNodeName, + DeclarationDiagnosticProducing, + GetSymbolAccessibilityDiagnostic, +} from "./declarations/diagnostics"; +import { getOriginalNodeId } from "./utilities"; /** @internal */ export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, file: SourceFile | undefined): DiagnosticWithLocation[] | undefined { @@ -624,7 +648,7 @@ export function transformDeclarations(context: TransformationContext) { } if (declFileName) { - const specifier = moduleSpecifiers.getModuleSpecifier( + const specifier = getModuleSpecifier( options, currentSourceFile, toPath(outputFilePath, host.getCurrentDirectory(), host.getCanonicalFileName), diff --git a/src/compiler/transformers/declarations/diagnostics.ts b/src/compiler/transformers/declarations/diagnostics.ts index 2e3154aa7f710..50e1b63e0fca0 100644 --- a/src/compiler/transformers/declarations/diagnostics.ts +++ b/src/compiler/transformers/declarations/diagnostics.ts @@ -1,22 +1,6 @@ +import * as Debug from "../../debug"; +import { Diagnostics } from "../../diagnosticInformationMap.generated"; import { - BinaryExpression, - BindingElement, - CallSignatureDeclaration, - ConstructorDeclaration, - ConstructSignatureDeclaration, - Debug, - Declaration, - DeclarationName, - DiagnosticMessage, - Diagnostics, - ElementAccessExpression, - ExpressionWithTypeArguments, - FunctionDeclaration, - GetAccessorDeclaration, - getNameOfDeclaration, - hasSyntacticModifier, - ImportEqualsDeclaration, - IndexSignatureDeclaration, isBinaryExpression, isBindingElement, isCallSignatureDeclaration, @@ -26,23 +10,34 @@ import { isElementAccessExpression, isExpressionWithTypeArguments, isFunctionDeclaration, - isGetAccessor, isHeritageClause, isImportEqualsDeclaration, isIndexSignatureDeclaration, - isJSDocTypeAlias, isMethodDeclaration, isMethodSignature, isParameter, - isParameterPropertyDeclaration, isPropertyAccessExpression, isPropertyDeclaration, isPropertySignature, - isSetAccessor, - isStatic, isTypeAliasDeclaration, isTypeParameterDeclaration, isVariableDeclaration, +} from "../../factory/nodeTests"; +import { + BinaryExpression, + BindingElement, + CallSignatureDeclaration, + ConstructorDeclaration, + ConstructSignatureDeclaration, + Declaration, + DeclarationName, + DiagnosticMessage, + ElementAccessExpression, + ExpressionWithTypeArguments, + FunctionDeclaration, + GetAccessorDeclaration, + ImportEqualsDeclaration, + IndexSignatureDeclaration, JSDocCallbackTag, JSDocEnumTag, JSDocTypedefTag, @@ -63,7 +58,18 @@ import { TypeAliasDeclaration, TypeParameterDeclaration, VariableDeclaration, -} from "../../_namespaces/ts"; +} from "../../types"; +import { + hasSyntacticModifier, + isJSDocTypeAlias, + isStatic, +} from "../../utilities"; +import { + getNameOfDeclaration, + isGetAccessor, + isParameterPropertyDeclaration, + isSetAccessor, +} from "../../utilitiesPublic"; /** @internal */ export type GetSymbolAccessibilityDiagnostic = (symbolAccessibilityResult: SymbolAccessibilityResult) => (SymbolAccessibilityDiagnostic | undefined); diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 27a991d0f3402..b59a983bc527c 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -1,66 +1,76 @@ import { - __String, addRange, append, + every, + forEach, + last, + map, + some, +} from "../core"; +import * as Debug from "../debug"; +import { + isBindingElement, + isComputedPropertyName, + isIdentifier, + isOmittedExpression, + isVariableDeclaration, +} from "../factory/nodeTests"; +import { + getElementsOfBindingOrAssignmentPattern, + getInitializerOfBindingOrAssignmentElement, + getPropertyNameOfBindingOrAssignmentElement, + getRestIndicatorOfBindingOrAssignmentElement, + getTargetOfBindingOrAssignmentElement, + tryGetPropertyNameOfBindingOrAssignmentElement, +} from "../factory/utilities"; +import { setTextRange } from "../factory/utilitiesPublic"; +import { + __String, ArrayBindingOrAssignmentPattern, BindingName, BindingOrAssignmentElement, BindingOrAssignmentElementTarget, BindingOrAssignmentPattern, - Debug, DestructuringAssignment, ElementAccessExpression, - every, Expression, - forEach, - getElementsOfBindingOrAssignmentPattern, - getInitializerOfBindingOrAssignmentElement, - getPropertyNameOfBindingOrAssignmentElement, - getRestIndicatorOfBindingOrAssignmentElement, - getTargetOfBindingOrAssignmentElement, Identifier, + LeftHandSideExpression, + Node, + NodeFactory, + ObjectBindingOrAssignmentPattern, + ParameterDeclaration, + PropertyName, + TextRange, + TransformationContext, + TransformFlags, + VariableDeclaration, + VisitResult, +} from "../types"; +import { + isDestructuringAssignment, + isEmptyArrayLiteral, + isEmptyObjectLiteral, + isPropertyNameLiteral, + isStringOrNumericLiteralLike, + nodeIsSynthesized, +} from "../utilities"; +import { idText, isArrayBindingElement, isArrayBindingOrAssignmentElement, isArrayBindingOrAssignmentPattern, - isBindingElement, isBindingName, isBindingOrAssignmentElement, isBindingOrAssignmentPattern, - isComputedPropertyName, isDeclarationBindingElement, - isDestructuringAssignment, - isEmptyArrayLiteral, - isEmptyObjectLiteral, isExpression, - isIdentifier, isLiteralExpression, isObjectBindingOrAssignmentElement, isObjectBindingOrAssignmentPattern, - isOmittedExpression, - isPropertyNameLiteral, - isSimpleInlineableExpression, - isStringOrNumericLiteralLike, - isVariableDeclaration, - last, - LeftHandSideExpression, - map, - Node, - NodeFactory, - nodeIsSynthesized, - ObjectBindingOrAssignmentPattern, - ParameterDeclaration, - PropertyName, - setTextRange, - some, - TextRange, - TransformationContext, - TransformFlags, - tryGetPropertyNameOfBindingOrAssignmentElement, - VariableDeclaration, - visitNode, - VisitResult, -} from "../_namespaces/ts"; +} from "../utilitiesPublic"; +import { visitNode } from "../visitorPublic"; +import { isSimpleInlineableExpression } from "./utilities"; interface FlattenContext { context: TransformationContext; diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index a6e9222b4e8da..aa94ae4382228 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -1,212 +1,234 @@ import { - __String, - AccessorDeclaration, - addEmitHelpers, addRange, - addSyntheticLeadingComment, - AllAccessorDeclarations, append, arrayIsEqualTo, - ArrayLiteralExpression, - ArrowFunction, - BinaryExpression, - BindingElement, - BindingPattern, - Block, - BreakOrContinueStatement, - Bundle, - CallExpression, - CaseBlock, - CaseClause, cast, - CatchClause, - chainBundle, - ClassDeclaration, - ClassElement, - ClassExpression, - ClassLikeDeclaration, - CommaListExpression, - ComputedPropertyName, concatenate, - ConstructorDeclaration, - createExpressionForPropertyName, - createMemberAccessForPropertyName, - createRange, - createTokenRange, - Debug, - Declaration, - DoStatement, elementAt, - EmitFlags, - EmitHint, emptyArray, - Expression, - ExpressionStatement, - ExpressionWithTypeArguments, filter, first, firstOrUndefined, flatten, - flattenDestructuringAssignment, - flattenDestructuringBinding, - FlattenLevel, - ForInStatement, - ForOfStatement, - ForStatement, - FunctionBody, - FunctionDeclaration, - FunctionExpression, - FunctionLikeDeclaration, - GeneratedIdentifierFlags, - getAllAccessorDeclarations, - getClassExtendsHeritageElement, - getCombinedNodeFlags, + last, + lastOrUndefined, + map, + singleOrMany, + singleOrUndefined, + some, + spanMap, + takeWhile, + tryCast, +} from "../core"; +import * as Debug from "../debug"; +import { isCallToHelper } from "../factory/emitHelpers"; +import { + addEmitHelpers, + addSyntheticLeadingComment, getCommentRange, - getEmitFlags, - getEnclosingBlockScopeContainer, - getFirstConstructorWithBody, - getInternalEmitFlags, - getNameOfDeclaration, - getOriginalNode, - getParseTreeNode, getSourceMapRange, - getSuperCallFromStatement, - getUseDefineForClassFields, - hasStaticModifier, - hasSyntacticModifier, - Identifier, - idText, - IfStatement, - insertStatementAfterCustomPrologue, - insertStatementsAfterCustomPrologue, - insertStatementsAfterStandardPrologue, - InternalEmitFlags, + moveSyntheticComments, + setCommentRange, + setEmitFlags, + setSourceMapRange, + setTokenSourceMapRange, +} from "../factory/emitNode"; +import { isArrayLiteralExpression, isArrowFunction, - isAssignmentExpression, isBinaryExpression, - isBindingPattern, isBlock, isCallExpression, - isCallToHelper, isCaseBlock, isCaseClause, isCatchClause, - isClassElement, - isClassLike, isComputedPropertyName, isDefaultClause, - isDestructuringAssignment, - isExpression, isExpressionStatement, - isForInitializer, isForStatement, isFunctionExpression, - isFunctionLike, - isHoistedFunction, - isHoistedVariableStatement, isIdentifier, - isIdentifierANonContextualKeyword, isIfStatement, - isInternalName, - isIterationStatement, isLabeledStatement, - isModifier, - isObjectLiteralElementLike, isOmittedExpression, - isPackedArrayLiteral, isPrivateIdentifier, - isPrologueDirective, isPropertyDeclaration, - isPropertyName, isReturnStatement, isSpreadElement, - isStatement, - isStatic, - isSuperProperty, isSwitchStatement, isTryStatement, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, isWithStatement, +} from "../factory/nodeTests"; +import { + createExpressionForPropertyName, + createMemberAccessForPropertyName, + isInternalName, + startOnNewLine, +} from "../factory/utilities"; +import { + setOriginalNode, + setTextRange, +} from "../factory/utilitiesPublic"; +import { setParent } from "../parserUtilities"; +import { skipTrivia } from "../scanner"; +import { + __String, + AccessorDeclaration, + AllAccessorDeclarations, + ArrayLiteralExpression, + ArrowFunction, + BinaryExpression, + BindingElement, + BindingPattern, + Block, + BreakOrContinueStatement, + Bundle, + CallExpression, + CaseBlock, + CaseClause, + CatchClause, + ClassDeclaration, + ClassElement, + ClassExpression, + ClassLikeDeclaration, + CommaListExpression, + ComputedPropertyName, + ConstructorDeclaration, + Declaration, + DoStatement, + EmitFlags, + EmitHint, + Expression, + ExpressionStatement, + ExpressionWithTypeArguments, + ForInStatement, + ForOfStatement, + ForStatement, + FunctionBody, + FunctionDeclaration, + FunctionExpression, + FunctionLikeDeclaration, + GeneratedIdentifierFlags, + Identifier, + IfStatement, + InternalEmitFlags, IterationStatement, LabeledStatement, - last, - lastOrUndefined, LeftHandSideExpression, LiteralExpression, - map, MetaProperty, MethodDeclaration, ModifierFlags, - moveRangeEnd, - moveRangePos, - moveSyntheticComments, NamedDeclaration, NewExpression, Node, NodeArray, NodeCheckFlags, NodeFlags, - nodeIsSynthesized, NumericLiteral, ObjectLiteralElementLike, ObjectLiteralExpression, ParameterDeclaration, ParenthesizedExpression, PrimaryExpression, - ProcessLevel, - processTaggedTemplateExpression, PropertyAssignment, - rangeEndIsOnSameLineAsRangeStart, ReturnStatement, SemicolonClassElement, - setCommentRange, - setEmitFlags, - setOriginalNode, - setParent, - setSourceMapRange, - setTextRange, - setTextRangeEnd, - setTextRangePos, - setTokenSourceMapRange, ShorthandPropertyAssignment, - singleOrMany, - singleOrUndefined, - skipOuterExpressions, - skipTrivia, - some, SourceFile, - spanMap, SpreadElement, - startOnNewLine, Statement, StringLiteral, SwitchStatement, SyntaxKind, TaggedTemplateExpression, - takeWhile, TemplateExpression, TextRange, TokenFlags, TransformationContext, TransformFlags, - tryCast, - unescapeLeadingUnderscores, - unwrapInnermostStatementOfLabel, VariableDeclaration, VariableDeclarationList, VariableStatement, - visitEachChild, - visitNode, - visitNodes, - visitParameterList, VisitResult, VoidExpression, WhileStatement, YieldExpression, -} from "../_namespaces/ts"; +} from "../types"; +import { + createRange, + createTokenRange, + getAllAccessorDeclarations, + getClassExtendsHeritageElement, + getEmitFlags, + getEnclosingBlockScopeContainer, + getFirstConstructorWithBody, + getInternalEmitFlags, + getUseDefineForClassFields, + hasStaticModifier, + hasSyntacticModifier, + insertStatementAfterCustomPrologue, + insertStatementsAfterCustomPrologue, + insertStatementsAfterStandardPrologue, + isAssignmentExpression, + isDestructuringAssignment, + isHoistedFunction, + isHoistedVariableStatement, + isIdentifierANonContextualKeyword, + isPackedArrayLiteral, + isPrologueDirective, + isStatic, + isSuperProperty, + moveRangeEnd, + moveRangePos, + nodeIsSynthesized, + rangeEndIsOnSameLineAsRangeStart, + setTextRangeEnd, + setTextRangePos, + skipOuterExpressions, + unwrapInnermostStatementOfLabel, +} from "../utilities"; +import { + getCombinedNodeFlags, + getNameOfDeclaration, + getOriginalNode, + getParseTreeNode, + idText, + isBindingPattern, + isClassElement, + isClassLike, + isExpression, + isForInitializer, + isFunctionLike, + isIterationStatement, + isModifier, + isObjectLiteralElementLike, + isPropertyName, + isStatement, + unescapeLeadingUnderscores, +} from "../utilitiesPublic"; +import { + visitEachChild, + visitNode, + visitNodes, + visitParameterList, +} from "../visitorPublic"; +import { + flattenDestructuringAssignment, + flattenDestructuringBinding, + FlattenLevel, +} from "./destructuring"; +import { + ProcessLevel, + processTaggedTemplateExpression, +} from "./taggedTemplate"; +import { + chainBundle, + getSuperCallFromStatement, +} from "./utilities"; const enum ES2015SubstitutionFlags { /** Enables substitutions for captured `this` */ diff --git a/src/compiler/transformers/es2016.ts b/src/compiler/transformers/es2016.ts index 33df5b94f0d0b..c534dbef5876c 100644 --- a/src/compiler/transformers/es2016.ts +++ b/src/compiler/transformers/es2016.ts @@ -1,21 +1,25 @@ +import { + isElementAccessExpression, + isPropertyAccessExpression, +} from "../factory/nodeTests"; +import { setTextRange } from "../factory/utilitiesPublic"; import { BinaryExpression, Bundle, - chainBundle, Expression, - isElementAccessExpression, - isExpression, - isPropertyAccessExpression, Node, - setTextRange, SourceFile, SyntaxKind, TransformationContext, TransformFlags, + VisitResult, +} from "../types"; +import { isExpression } from "../utilitiesPublic"; +import { visitEachChild, visitNode, - VisitResult, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { chainBundle } from "./utilities"; /** @internal */ export function transformES2016(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 1c1a2e9f45f23..ebaa43e51f76b 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -1,29 +1,50 @@ import { - __String, - AccessorDeclaration, + concatenate, + forEach, + map, + some, +} from "../core"; +import * as Debug from "../debug"; +import { + advancedAsyncSuperHelper, + asyncSuperHelper, +} from "../factory/emitHelpers"; +import { addEmitHelper, addEmitHelpers, - advancedAsyncSuperHelper, + setEmitFlags, + setSourceMapRange, +} from "../factory/emitNode"; +import { + isAwaitKeyword, + isBlock, + isIdentifier, + isOmittedExpression, + isPropertyAccessExpression, + isVariableDeclarationList, +} from "../factory/nodeTests"; +import { + setOriginalNode, + setTextRange, +} from "../factory/utilitiesPublic"; +import { + __String, + AccessorDeclaration, ArrowFunction, - asyncSuperHelper, AwaitExpression, BindingElement, Block, Bundle, CallExpression, CatchClause, - chainBundle, ClassDeclaration, - concatenate, ConciseBody, ConstructorDeclaration, - Debug, ElementAccessExpression, EmitFlags, EmitHint, EmitResolver, Expression, - forEach, ForInitializer, ForInStatement, ForOfStatement, @@ -31,37 +52,10 @@ import { FunctionBody, FunctionDeclaration, FunctionExpression, - FunctionFlags, FunctionLikeDeclaration, GeneratedIdentifierFlags, GetAccessorDeclaration, - getEmitScriptTarget, - getEntityNameFromTypeNode, - getFunctionFlags, - getInitializedVariables, - getNodeId, - getOriginalNode, - insertStatementsAfterStandardPrologue, - isAwaitKeyword, - isBlock, - isConciseBody, - isEffectiveStrictModeSourceFile, - isEntityName, - isExpression, - isForInitializer, - isFunctionLike, - isFunctionLikeDeclaration, - isIdentifier, - isModifier, - isModifierLike, - isNodeWithPossibleHoistedDeclaration, - isOmittedExpression, - isPropertyAccessExpression, - isStatement, - isSuperProperty, - isVariableDeclarationList, LeftHandSideExpression, - map, MethodDeclaration, Node, NodeCheckFlags, @@ -72,11 +66,6 @@ import { PropertyAssignment, ScriptTarget, SetAccessorDeclaration, - setEmitFlags, - setOriginalNode, - setSourceMapRange, - setTextRange, - some, SourceFile, Statement, SyntaxKind, @@ -85,18 +74,44 @@ import { TransformFlags, TypeNode, TypeReferenceSerializationKind, - unescapeLeadingUnderscores, VariableDeclaration, VariableDeclarationList, VariableStatement, + VisitResult, +} from "../types"; +import { + FunctionFlags, + getEmitScriptTarget, + getEntityNameFromTypeNode, + getFunctionFlags, + getInitializedVariables, + getNodeId, + insertStatementsAfterStandardPrologue, + isNodeWithPossibleHoistedDeclaration, + isSuperProperty, +} from "../utilities"; +import { + getOriginalNode, + isConciseBody, + isEntityName, + isExpression, + isForInitializer, + isFunctionLike, + isFunctionLikeDeclaration, + isModifier, + isModifierLike, + isStatement, + unescapeLeadingUnderscores, +} from "../utilitiesPublic"; +import { visitEachChild, visitFunctionBody, visitIterationBody, visitNode, visitNodes, visitParameterList, - VisitResult, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { chainBundle, isEffectiveStrictModeSourceFile } from "./utilities"; type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration; diff --git a/src/compiler/transformers/es2018.ts b/src/compiler/transformers/es2018.ts index 00ca8babafec4..2e6f2c7bc3138 100644 --- a/src/compiler/transformers/es2018.ts +++ b/src/compiler/transformers/es2018.ts @@ -1,71 +1,65 @@ import { - __String, - AccessorDeclaration, + addRange, + append, + concatenate, + some, +} from "../core"; +import * as Debug from "../debug"; +import { + advancedAsyncSuperHelper, + asyncSuperHelper, +} from "../factory/emitHelpers"; +import { addEmitFlags, addEmitHelper, addEmitHelpers, - addRange, - advancedAsyncSuperHelper, - append, + setEmitFlags, + setSourceMapRange, +} from "../factory/emitNode"; +import { + isBlock, + isIdentifier, + isParameter, + isPropertyAccessExpression, + isQuestionToken, + isVariableDeclarationList, +} from "../factory/nodeTests"; +import { + containsObjectRestOrSpread, + createForOfBindingStatement, + startOnNewLine, +} from "../factory/utilities"; +import { + setOriginalNode, + setTextRange, +} from "../factory/utilitiesPublic"; +import { + __String, + AccessorDeclaration, ArrowFunction, - asyncSuperHelper, AwaitExpression, BinaryExpression, Bundle, CallExpression, CatchClause, - chainBundle, CommaListExpression, - concatenate, ConciseBody, ConstructorDeclaration, - containsObjectRestOrSpread, - createForOfBindingStatement, - createSuperAccessVariableStatement, - Debug, ElementAccessExpression, EmitFlags, EmitHint, Expression, ExpressionStatement, - flattenDestructuringAssignment, - flattenDestructuringBinding, - FlattenLevel, ForInitializer, ForOfStatement, ForStatement, FunctionBody, FunctionDeclaration, FunctionExpression, - FunctionFlags, FunctionLikeDeclaration, GeneratedIdentifierFlags, GetAccessorDeclaration, - getEmitScriptTarget, - getFunctionFlags, - getNodeId, - hasSyntacticModifier, Identifier, - insertStatementsAfterStandardPrologue, - isAssignmentPattern, - isBindingPattern, - isBlock, - isConciseBody, - isDestructuringAssignment, - isEffectiveStrictModeSourceFile, - isExpression, - isForInitializer, - isIdentifier, - isModifier, - isModifierLike, - isObjectLiteralElementLike, - isParameter, - isPropertyAccessExpression, - isPropertyName, - isQuestionToken, - isStatement, - isSuperProperty, - isVariableDeclarationList, LabeledStatement, LeftHandSideExpression, MethodDeclaration, @@ -77,40 +71,67 @@ import { ObjectLiteralExpression, ParameterDeclaration, ParenthesizedExpression, - ProcessLevel, - processTaggedTemplateExpression, PropertyAccessExpression, ReturnStatement, ScriptTarget, SetAccessorDeclaration, - setEmitFlags, - setOriginalNode, - setSourceMapRange, - setTextRange, SignatureDeclaration, - skipParentheses, - some, SourceFile, - startOnNewLine, Statement, SyntaxKind, TaggedTemplateExpression, TextRange, TransformationContext, TransformFlags, - unwrapInnermostStatementOfLabel, VariableDeclaration, VariableStatement, + VisitResult, + VoidExpression, + YieldExpression, +} from "../types"; +import { + FunctionFlags, + getEmitScriptTarget, + getFunctionFlags, + getNodeId, + hasSyntacticModifier, + insertStatementsAfterStandardPrologue, + isDestructuringAssignment, + isSuperProperty, + skipParentheses, + unwrapInnermostStatementOfLabel, +} from "../utilities"; +import { + isAssignmentPattern, + isBindingPattern, + isConciseBody, + isExpression, + isForInitializer, + isModifier, + isModifierLike, + isObjectLiteralElementLike, + isPropertyName, + isStatement, +} from "../utilitiesPublic"; +import { visitEachChild, visitIterationBody, visitLexicalEnvironment, visitNode, visitNodes, visitParameterList, - VisitResult, - VoidExpression, - YieldExpression, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { + flattenDestructuringAssignment, + flattenDestructuringBinding, + FlattenLevel, +} from "./destructuring"; +import { createSuperAccessVariableStatement } from "./es2017"; +import { + ProcessLevel, + processTaggedTemplateExpression, +} from "./taggedTemplate"; +import { chainBundle, isEffectiveStrictModeSourceFile } from "./utilities"; const enum ESNextSubstitutionFlags { /** Enables substitutions for async methods with `super` calls. */ diff --git a/src/compiler/transformers/es2019.ts b/src/compiler/transformers/es2019.ts index 0bd2be45422a4..887050281f642 100644 --- a/src/compiler/transformers/es2019.ts +++ b/src/compiler/transformers/es2019.ts @@ -1,17 +1,19 @@ +import { isBlock } from "../factory/nodeTests"; import { Bundle, CatchClause, - chainBundle, - isBlock, Node, SourceFile, SyntaxKind, TransformationContext, TransformFlags, + VisitResult, +} from "../types"; +import { visitEachChild, visitNode, - VisitResult, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { chainBundle } from "./utilities"; /** @internal */ export function transformES2019(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { diff --git a/src/compiler/transformers/es2020.ts b/src/compiler/transformers/es2020.ts index b030dca03ce5c..a20c6b8575af7 100644 --- a/src/compiler/transformers/es2020.ts +++ b/src/compiler/transformers/es2020.ts @@ -1,42 +1,52 @@ +import { cast } from "../core"; +import * as Debug from "../debug"; +import { addEmitFlags } from "../factory/emitNode"; +import { + isIdentifier, + isParenthesizedExpression, + isSyntheticReference, + isTaggedTemplateExpression, +} from "../factory/nodeTests"; +import { + setOriginalNode, + setTextRange, +} from "../factory/utilitiesPublic"; import { AccessExpression, - addEmitFlags, BinaryExpression, Bundle, CallExpression, - cast, - chainBundle, - Debug, DeleteExpression, EmitFlags, Expression, - isCallChain, - isExpression, - isGeneratedIdentifier, - isIdentifier, - isNonNullChain, - isOptionalChain, - isParenthesizedExpression, - isSimpleCopiableExpression, - isSyntheticReference, - isTaggedTemplateExpression, Node, OptionalChain, OuterExpressionKinds, ParenthesizedExpression, - setOriginalNode, - setTextRange, - skipParentheses, - skipPartiallyEmittedExpressions, SourceFile, SyntaxKind, TransformationContext, TransformFlags, + VisitResult, +} from "../types"; +import { skipParentheses } from "../utilities"; +import { + isCallChain, + isExpression, + isGeneratedIdentifier, + isNonNullChain, + isOptionalChain, + skipPartiallyEmittedExpressions, +} from "../utilitiesPublic"; +import { visitEachChild, visitNode, visitNodes, - VisitResult, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { + chainBundle, + isSimpleCopiableExpression, +} from "./utilities"; /** @internal */ export function transformES2020(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { diff --git a/src/compiler/transformers/es2021.ts b/src/compiler/transformers/es2021.ts index b2fbd0fbe71b2..0e7c1a4583706 100644 --- a/src/compiler/transformers/es2021.ts +++ b/src/compiler/transformers/es2021.ts @@ -1,25 +1,33 @@ +import { isPropertyAccessExpression } from "../factory/nodeTests"; import { AssignmentExpression, Bundle, - chainBundle, - getNonAssignmentOperatorForCompoundAssignment, - isAccessExpression, - isExpression, - isLeftHandSideExpression, - isLogicalOrCoalescingAssignmentExpression, - isPropertyAccessExpression, - isSimpleCopiableExpression, LogicalOrCoalescingAssignmentOperator, Node, - skipParentheses, SourceFile, Token, TransformationContext, TransformFlags, + VisitResult, +} from "../types"; +import { + isAccessExpression, + isLogicalOrCoalescingAssignmentExpression, + skipParentheses, +} from "../utilities"; +import { + isExpression, + isLeftHandSideExpression, +} from "../utilitiesPublic"; +import { visitEachChild, visitNode, - VisitResult, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { + chainBundle, + getNonAssignmentOperatorForCompoundAssignment, + isSimpleCopiableExpression, +} from "./utilities"; /** @internal */ export function transformES2021(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { diff --git a/src/compiler/transformers/es5.ts b/src/compiler/transformers/es5.ts index 24485e86c95e7..666f147fb2d03 100644 --- a/src/compiler/transformers/es5.ts +++ b/src/compiler/transformers/es5.ts @@ -1,15 +1,15 @@ import { - Bundle, - chainBundle, - EmitHint, - Expression, - getOriginalNodeId, - Identifier, - identifierToKeywordKind, isIdentifier, isPrivateIdentifier, isPropertyAccessExpression, isPropertyAssignment, +} from "../factory/nodeTests"; +import { setTextRange } from "../factory/utilitiesPublic"; +import { + Bundle, + EmitHint, + Expression, + Identifier, JsxClosingElement, JsxEmit, JsxOpeningElement, @@ -17,11 +17,15 @@ import { Node, PropertyAccessExpression, PropertyAssignment, - setTextRange, SourceFile, SyntaxKind, TransformationContext, -} from "../_namespaces/ts"; +} from "../types"; +import { identifierToKeywordKind } from "../utilitiesPublic"; +import { + chainBundle, + getOriginalNodeId, +} from "./utilities"; /** * Transforms ES5 syntax into ES3 syntax. diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 730958dae1e00..c6e001e76d0db 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -1,13 +1,13 @@ import { Bundle, - chainBundle, Node, SourceFile, TransformationContext, TransformFlags, - visitEachChild, VisitResult, -} from "../_namespaces/ts"; +} from "../types"; +import { visitEachChild } from "../visitorPublic"; +import { chainBundle } from "./utilities"; /** @internal */ export function transformESNext(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index cf15cb43c326a..8170265dd5fae 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -1,62 +1,60 @@ import { - AccessorDeclaration, + forEach, + lastOrUndefined, + map, + reduceLeft, +} from "../core"; +import * as Debug from "../debug"; +import { addEmitHelpers, addSyntheticTrailingComment, + setCommentRange, + setEmitFlags, + setSourceMapRange, +} from "../factory/emitNode"; +import { + isBinaryExpression, + isBlock, + isIdentifier, + isVariableDeclarationList, +} from "../factory/nodeTests"; +import { + createExpressionForObjectLiteralElementLike, + startOnNewLine, +} from "../factory/utilities"; +import { + setOriginalNode, + setTextRange, +} from "../factory/utilitiesPublic"; +import { setParent } from "../parserUtilities"; +import { + AccessorDeclaration, ArrayLiteralExpression, - Associativity, BinaryExpression, Block, BreakStatement, Bundle, CallExpression, CaseClause, - chainBundle, CommaListExpression, ConditionalExpression, ContinueStatement, - createExpressionForObjectLiteralElementLike, - Debug, DoStatement, ElementAccessExpression, EmitFlags, EmitHint, Expression, ExpressionStatement, - forEach, ForInStatement, ForStatement, FunctionDeclaration, FunctionExpression, - getEmitFlags, - getEmitScriptTarget, - getExpressionAssociativity, - getInitializedVariables, - getNonAssignmentOperatorForCompoundAssignment, - getOriginalNode, - getOriginalNodeId, Identifier, - idText, IfStatement, InitializedVariableDeclaration, - insertStatementsAfterStandardPrologue, - isBinaryExpression, - isBlock, - isCompoundAssignment, - isExpression, - isFunctionLikeDeclaration, - isGeneratedIdentifier, - isIdentifier, - isImportCall, - isLeftHandSideExpression, - isLogicalOperator, - isObjectLiteralElementLike, - isStatement, - isVariableDeclarationList, LabeledStatement, - lastOrUndefined, LeftHandSideExpression, LiteralExpression, - map, Mutable, NewExpression, Node, @@ -65,16 +63,8 @@ import { ObjectLiteralElementLike, ObjectLiteralExpression, PropertyAccessExpression, - reduceLeft, ReturnStatement, - setCommentRange, - setEmitFlags, - setOriginalNode, - setParent, - setSourceMapRange, - setTextRange, SourceFile, - startOnNewLine, Statement, SwitchStatement, SyntaxKind, @@ -86,16 +76,44 @@ import { VariableDeclaration, VariableDeclarationList, VariableStatement, + VisitResult, + WhileStatement, + WithStatement, + YieldExpression, +} from "../types"; +import { + Associativity, + getEmitFlags, + getEmitScriptTarget, + getExpressionAssociativity, + getInitializedVariables, + insertStatementsAfterStandardPrologue, + isImportCall, + isLogicalOperator, +} from "../utilities"; +import { + getOriginalNode, + idText, + isExpression, + isFunctionLikeDeclaration, + isGeneratedIdentifier, + isLeftHandSideExpression, + isObjectLiteralElementLike, + isStatement, +} from "../utilitiesPublic"; +import { visitEachChild, visitIterationBody, visitNode, visitNodes, visitParameterList, - VisitResult, - WhileStatement, - WithStatement, - YieldExpression, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { + chainBundle, + getNonAssignmentOperatorForCompoundAssignment, + getOriginalNodeId, + isCompoundAssignment, +} from "./utilities"; // Transforms generator functions into a compatible ES5 representation with similar runtime // semantics. This is accomplished by first transforming the body of each generator diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 52fa0bc564f74..5ec180118526b 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -1,48 +1,55 @@ import { - addEmitHelpers, arrayFrom, - Bundle, - chainBundle, - createExpressionForJsxElement, - createExpressionForJsxFragment, - createExpressionFromEntityName, - createJsxFactoryExpression, - Debug, emptyArray, - Expression, filter, find, flatten, - GeneratedIdentifierFlags, - getEmitScriptTarget, - getJSXImplicitImportBase, - getJSXRuntimeImport, - getLineAndCharacterOfPosition, - getOriginalNode, - getSemanticJsxChildren, - Identifier, - idText, - ImportSpecifier, - insertStatementAfterCustomPrologue, - isExpression, - isExternalModule, - isExternalOrCommonJsModule, + isLineBreak, + isWhiteSpaceSingleLine, + length, + map, + mapDefined, + singleOrUndefined, + spanMap, +} from "../core"; +import * as Debug from "../debug"; +import { + addEmitHelpers, + setIdentifierGeneratedImportReference, +} from "../factory/emitNode"; +import { isIdentifier, - isIntrinsicJsxName, isJsxAttribute, isJsxElement, isJsxFragment, isJsxNamespacedName, isJsxSelfClosingElement, isJsxSpreadAttribute, - isLineBreak, isObjectLiteralExpression, isPropertyAssignment, isSourceFile, isSpreadAssignment, - isStringDoubleQuoted, isStringLiteral, - isWhiteSpaceSingleLine, +} from "../factory/nodeTests"; +import { + createExpressionForJsxElement, + createExpressionForJsxFragment, + createExpressionFromEntityName, + createJsxFactoryExpression, + startOnNewLine, +} from "../factory/utilities"; +import { setTextRange } from "../factory/utilitiesPublic"; +import { setParentRecursive } from "../parserUtilities"; +import { + getLineAndCharacterOfPosition, + utf16EncodeAsString, +} from "../scanner"; +import { + Bundle, + Expression, + GeneratedIdentifierFlags, + Identifier, + ImportSpecifier, JsxAttribute, JsxAttributeValue, JsxChild, @@ -55,34 +62,43 @@ import { JsxSelfClosingElement, JsxSpreadAttribute, JsxText, - length, - map, - mapDefined, Node, NodeFlags, ObjectLiteralElementLike, ObjectLiteralExpression, PropertyAssignment, ScriptTarget, - setIdentifierGeneratedImportReference, - setParentRecursive, - setTextRange, - singleOrUndefined, SourceFile, - spanMap, - startOnNewLine, Statement, StringLiteral, SyntaxKind, TextRange, TransformationContext, TransformFlags, - utf16EncodeAsString, VariableDeclaration, + VisitResult, +} from "../types"; +import { + getEmitScriptTarget, + getJSXImplicitImportBase, + getJSXRuntimeImport, + getSemanticJsxChildren, + insertStatementAfterCustomPrologue, + isExternalModule, + isExternalOrCommonJsModule, + isIntrinsicJsxName, + isStringDoubleQuoted, +} from "../utilities"; +import { + getOriginalNode, + idText, + isExpression, +} from "../utilitiesPublic"; +import { visitEachChild, visitNode, - VisitResult, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { chainBundle } from "./utilities"; /** @internal */ export function transformJsx(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { diff --git a/src/compiler/transformers/legacyDecorators.ts b/src/compiler/transformers/legacyDecorators.ts index f175fe71a1951..5656481d4500c 100644 --- a/src/compiler/transformers/legacyDecorators.ts +++ b/src/compiler/transformers/legacyDecorators.ts @@ -1,89 +1,104 @@ import { - __String, - addEmitHelpers, addRange, - AllDecorators, append, - Bundle, + filter, + flatMap, + groupBy, + map, + singleOrMany, + some, +} from "../core"; +import * as Debug from "../debug"; +import { isCallToHelper } from "../factory/emitHelpers"; +import { + addEmitHelpers, + setCommentRange, + setEmitFlags, + setSourceMapRange, +} from "../factory/emitNode"; +import { + isBlock, + isClassStaticBlockDeclaration, + isComputedPropertyName, + isDecorator, + isHeritageClause, + isIdentifier, + isParameter, + isPrivateIdentifier, + isPropertyDeclaration, +} from "../factory/nodeTests"; +import { elideNodes, isExportOrDefaultModifier } from "../factory/utilities"; +import { canHaveDecorators, - chainBundle, - childIsDecorated, + setOriginalNode, + setTextRange, +} from "../factory/utilitiesPublic"; +import { + __String, + AllDecorators, + Bundle, ClassDeclaration, ClassElement, ClassExpression, ClassLikeDeclaration, - classOrConstructorParameterIsDecorated, ConstructorDeclaration, - Debug, Decorator, - elideNodes, EmitFlags, EmitHint, EnumMember, Expression, - filter, - flatMap, GetAccessorDeclaration, - getAllDecoratorsOfClass, - getAllDecoratorsOfClassElement, - getEmitScriptTarget, - getOriginalNodeId, - groupBy, - hasAccessorModifier, - hasSyntacticModifier, Identifier, - idText, - isBindingName, - isBlock, - isCallToHelper, - isClassElement, - isClassStaticBlockDeclaration, - isComputedPropertyName, - isDecorator, - isExportOrDefaultModifier, - isExpression, - isGeneratedIdentifier, - isHeritageClause, - isIdentifier, - isModifier, - isModifierLike, - isParameter, - isPrivateIdentifier, - isPropertyDeclaration, - isPropertyName, - isSimpleInlineableExpression, - isStatic, - map, MethodDeclaration, Modifier, ModifierFlags, - moveRangePastModifiers, Node, NodeArray, NodeCheckFlags, NodeFlags, - nodeOrChildIsDecorated, ParameterDeclaration, PropertyDeclaration, ScriptTarget, SetAccessorDeclaration, - setCommentRange, - setEmitFlags, - setOriginalNode, - setSourceMapRange, - setTextRange, - singleOrMany, - some, SourceFile, Statement, SyntaxKind, TransformationContext, TransformFlags, + VisitResult, +} from "../types"; +import { + childIsDecorated, + classOrConstructorParameterIsDecorated, + getEmitScriptTarget, + hasAccessorModifier, + hasSyntacticModifier, + isStatic, + moveRangePastModifiers, + nodeOrChildIsDecorated, +} from "../utilities"; +import { + idText, + isBindingName, + isClassElement, + isExpression, + isGeneratedIdentifier, + isModifier, + isModifierLike, + isPropertyName, +} from "../utilitiesPublic"; +import { visitEachChild, visitNode, visitNodes, - VisitResult, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { + chainBundle, + getAllDecoratorsOfClass, + getAllDecoratorsOfClassElement, + getOriginalNodeId, + isSimpleInlineableExpression, +} from "./utilities"; /** @internal */ export function transformLegacyDecorators(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { diff --git a/src/compiler/transformers/module/esnextAnd2015.ts b/src/compiler/transformers/module/esnextAnd2015.ts index 879544063f9f0..307631f288276 100644 --- a/src/compiler/transformers/module/esnextAnd2015.ts +++ b/src/compiler/transformers/module/esnextAnd2015.ts @@ -1,54 +1,68 @@ import { addRange, append, - Bundle, - chainBundle, + singleOrMany, + some, +} from "../../core"; +import * as Debug from "../../debug"; +import { + isIdentifier, + isNamespaceExport, + isSourceFile, +} from "../../factory/nodeTests"; +import { createEmptyExports, createExternalHelpersImportDeclarationIfNeeded, - Debug, + getExternalModuleNameLiteral, +} from "../../factory/utilities"; +import { + setOriginalNode, + setTextRange, +} from "../../factory/utilitiesPublic"; +import { + Bundle, EmitFlags, EmitHint, ExportAssignment, ExportDeclaration, Expression, GeneratedIdentifierFlags, - getEmitFlags, - getEmitModuleKind, - getEmitScriptTarget, - getExternalModuleNameLiteral, - getIsolatedModules, - hasSyntacticModifier, Identifier, - idText, ImportDeclaration, ImportEqualsDeclaration, - insertStatementsAfterCustomPrologue, - isExportNamespaceAsDefaultDeclaration, - isExternalModule, - isExternalModuleImportEqualsDeclaration, - isExternalModuleIndicator, - isIdentifier, - isNamespaceExport, - isSourceFile, - isStatement, ModifierFlags, ModuleKind, Node, NodeFlags, ScriptTarget, - setOriginalNode, - setTextRange, - singleOrMany, - some, SourceFile, Statement, SyntaxKind, TransformationContext, VariableStatement, + VisitResult, +} from "../../types"; +import { + getEmitFlags, + getEmitModuleKind, + getEmitScriptTarget, + getIsolatedModules, + hasSyntacticModifier, + insertStatementsAfterCustomPrologue, + isExportNamespaceAsDefaultDeclaration, + isExternalModule, + isExternalModuleImportEqualsDeclaration, +} from "../../utilities"; +import { + idText, + isExternalModuleIndicator, + isStatement, +} from "../../utilitiesPublic"; +import { visitEachChild, visitNodes, - VisitResult, -} from "../../_namespaces/ts"; +} from "../../visitorPublic"; +import { chainBundle } from "../utilities"; /** @internal */ export function transformECMAScriptModule(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index fc95446ce54c2..cd7c8fb557d7c 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -1,10 +1,61 @@ import { - addEmitHelper, - addEmitHelpers, - addInternalEmitFlags, addRange, append, arrayFrom, + emptyArray, + firstOrUndefined, + length, + mapDefined, + reduceLeft, + singleOrMany, + some, +} from "../../core"; +import * as Debug from "../../debug"; +import { + addEmitHelper, + addEmitHelpers, + addInternalEmitFlags, + removeAllComments, + setEmitFlags, +} from "../../factory/emitNode"; +import { + isArrayLiteralExpression, + isArrowFunction, + isBlock, + isCaseBlock, + isClassExpression, + isExportDeclaration, + isFunctionExpression, + isHeritageClause, + isIdentifier, + isImportClause, + isImportEqualsDeclaration, + isImportSpecifier, + isNamedExports, + isObjectLiteralExpression, + isOmittedExpression, + isParameter, + isPrefixUnaryExpression, + isShorthandPropertyAssignment, + isSpreadElement, + isStringLiteral, + isVariableDeclaration, + isVariableDeclarationList, +} from "../../factory/nodeTests"; +import { + getExternalHelpersModuleName, + getExternalModuleNameLiteral, + getLocalNameForExternalImport, + isExportName, + isLocalName, + startOnNewLine, + tryGetModuleNameFromFile, +} from "../../factory/utilities"; +import { + setOriginalNode, + setTextRange, +} from "../../factory/utilitiesPublic"; +import { ArrowFunction, BinaryExpression, BindingElement, @@ -14,10 +65,7 @@ import { CaseBlock, CaseClause, CatchClause, - chainBundle, ClassDeclaration, - collectExternalModuleInfo, - Debug, Declaration, DefaultClause, DestructuringAssignment, @@ -25,97 +73,24 @@ import { EmitFlags, EmitHelper, EmitHint, - emptyArray, ExportAssignment, ExportDeclaration, Expression, ExpressionStatement, - ExternalModuleInfo, - firstOrUndefined, - flattenDestructuringAssignment, - FlattenLevel, ForInStatement, ForOfStatement, ForStatement, FunctionDeclaration, FunctionExpression, GeneratedIdentifierFlags, - getEmitFlags, - getEmitModuleKind, - getEmitScriptTarget, - getESModuleInterop, - getExportNeedsImportStarHelper, - getExternalHelpersModuleName, - getExternalModuleNameLiteral, - getImportNeedsImportDefaultHelper, - getImportNeedsImportStarHelper, - getInternalEmitFlags, - getLocalNameForExternalImport, - getNamespaceDeclarationNode, - getNodeId, - getOriginalNodeId, - getStrictOptionValue, - getTextOfIdentifierOrLiteral, - hasJsonModuleEmitEnabled, - hasSyntacticModifier, Identifier, - idText, IfStatement, ImportCall, ImportDeclaration, ImportEqualsDeclaration, InitializedVariableDeclaration, - insertStatementsAfterStandardPrologue, InternalEmitFlags, - isArrayLiteralExpression, - isArrowFunction, - isAssignmentOperator, - isBindingPattern, - isBlock, - isCaseBlock, - isCaseOrDefaultClause, - isClassElement, - isClassExpression, - isDeclarationNameOfEnumOrNamespace, - isDefaultImport, - isDestructuringAssignment, - isEffectiveExternalModule, - isExportDeclaration, - isExportName, - isExportNamespaceAsDefaultDeclaration, - isExpression, - isExternalModule, - isExternalModuleImportEqualsDeclaration, - isForInitializer, - isFunctionExpression, - isGeneratedIdentifier, - isHeritageClause, - isIdentifier, - isImportCall, - isImportClause, - isImportEqualsDeclaration, - isImportSpecifier, - isInitializedVariable, - isJsonSourceFile, - isLocalName, - isModifier, - isModifierLike, - isNamedExports, - isObjectLiteralExpression, - isOmittedExpression, - isParameter, - isPrefixUnaryExpression, - isShorthandPropertyAssignment, - isSimpleCopiableExpression, - isSimpleInlineableExpression, - isSpreadElement, - isStatement, - isStringLiteral, - isVariableDeclaration, - isVariableDeclarationList, LabeledStatement, - length, - mapDefined, Modifier, ModifierFlags, ModuleKind, @@ -123,23 +98,14 @@ import { NodeArray, NodeFlags, ObjectLiteralElementLike, - outFile, ParameterDeclaration, ParenthesizedExpression, PartiallyEmittedExpression, PostfixUnaryExpression, PrefixUnaryExpression, - reduceLeft, - removeAllComments, ScriptTarget, - setEmitFlags, - setOriginalNode, - setTextRange, ShorthandPropertyAssignment, - singleOrMany, - some, SourceFile, - startOnNewLine, Statement, SwitchStatement, SyntaxKind, @@ -147,19 +113,73 @@ import { TextRange, TransformationContext, TransformFlags, - tryGetModuleNameFromFile, TryStatement, VariableDeclaration, VariableDeclarationList, VariableStatement, + VisitResult, + WhileStatement, + WithStatement, +} from "../../types"; +import { + getEmitFlags, + getEmitModuleKind, + getEmitScriptTarget, + getESModuleInterop, + getInternalEmitFlags, + getNamespaceDeclarationNode, + getNodeId, + getStrictOptionValue, + getTextOfIdentifierOrLiteral, + hasJsonModuleEmitEnabled, + hasSyntacticModifier, + insertStatementsAfterStandardPrologue, + isAssignmentOperator, + isDeclarationNameOfEnumOrNamespace, + isDefaultImport, + isDestructuringAssignment, + isEffectiveExternalModule, + isExportNamespaceAsDefaultDeclaration, + isExternalModule, + isExternalModuleImportEqualsDeclaration, + isImportCall, + isInitializedVariable, + isJsonSourceFile, + outFile, +} from "../../utilities"; +import { + idText, + isBindingPattern, + isCaseOrDefaultClause, + isClassElement, + isExpression, + isForInitializer, + isGeneratedIdentifier, + isModifier, + isModifierLike, + isStatement, +} from "../../utilitiesPublic"; +import { visitEachChild, visitIterationBody, visitNode, visitNodes, - VisitResult, - WhileStatement, - WithStatement, -} from "../../_namespaces/ts"; +} from "../../visitorPublic"; +import { + flattenDestructuringAssignment, + FlattenLevel, +} from "../destructuring"; +import { + chainBundle, + collectExternalModuleInfo, + ExternalModuleInfo, + getExportNeedsImportStarHelper, + getImportNeedsImportDefaultHelper, + getImportNeedsImportStarHelper, + getOriginalNodeId, + isSimpleCopiableExpression, + isSimpleInlineableExpression, +} from "../utilities"; /** @internal */ export function transformModule(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { diff --git a/src/compiler/transformers/module/node.ts b/src/compiler/transformers/module/node.ts index 576c15fe6597e..36c9b5216a8f9 100644 --- a/src/compiler/transformers/module/node.ts +++ b/src/compiler/transformers/module/node.ts @@ -1,17 +1,17 @@ +import { map } from "../../core"; +import * as Debug from "../../debug"; +import { isSourceFile } from "../../factory/nodeTests"; import { Bundle, - Debug, EmitHint, - isSourceFile, - map, ModuleKind, Node, SourceFile, SyntaxKind, TransformationContext, - transformECMAScriptModule, - transformModule, -} from "../../_namespaces/ts"; +} from "../../types"; +import { transformECMAScriptModule } from "./esnextAnd2015"; +import { transformModule } from "./module"; /** @internal */ export function transformNodeModule(context: TransformationContext) { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 4e2d9b954e626..c0e15097ca3dc 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1,6 +1,47 @@ import { addRange, append, + firstOrUndefined, + forEach, + map, + singleOrMany, + some, +} from "../../core"; +import * as Debug from "../../debug"; +import { + moveEmitHelpers, + setCommentRange, + setEmitFlags, +} from "../../factory/emitNode"; +import { + isArrayLiteralExpression, + isBlock, + isCaseBlock, + isHeritageClause, + isIdentifier, + isImportClause, + isImportSpecifier, + isNamedExports, + isObjectLiteralExpression, + isOmittedExpression, + isParameter, + isPrefixUnaryExpression, + isPropertyAssignment, + isShorthandPropertyAssignment, + isSpreadElement, + isStringLiteral, + isVariableDeclarationList, +} from "../../factory/nodeTests"; +import { + getExternalHelpersModuleName, + getExternalModuleNameLiteral, + getLocalNameForExternalImport, + isLocalName, + startOnNewLine, + tryGetModuleNameFromFile, +} from "../../factory/utilities"; +import { setTextRange } from "../../factory/utilitiesPublic"; +import { BinaryExpression, BindingElement, Block, @@ -9,10 +50,7 @@ import { CaseClause, CaseOrDefaultClause, CatchClause, - chainBundle, ClassDeclaration, - collectExternalModuleInfo, - Debug, Declaration, DefaultClause, DestructuringAssignment, @@ -23,90 +61,29 @@ import { ExportDeclaration, Expression, ExpressionStatement, - ExternalModuleInfo, - firstOrUndefined, - flattenDestructuringAssignment, - FlattenLevel, - forEach, ForInitializer, ForInStatement, ForOfStatement, ForStatement, FunctionDeclaration, - getEmitFlags, - getExternalHelpersModuleName, - getExternalModuleNameLiteral, - getLocalNameForExternalImport, - getNodeId, - getOriginalNode, - getOriginalNodeId, - getStrictOptionValue, - getTextOfIdentifierOrLiteral, - hasSyntacticModifier, Identifier, - idText, IfStatement, ImportCall, ImportDeclaration, ImportEqualsDeclaration, - insertStatementsAfterStandardPrologue, - isArrayLiteralExpression, - isAssignmentExpression, - isAssignmentOperator, - isBindingPattern, - isBlock, - isCaseBlock, - isCaseOrDefaultClause, - isClassElement, - isDeclarationNameOfEnumOrNamespace, - isDestructuringAssignment, - isEffectiveExternalModule, - isExpression, - isExternalModule, - isExternalModuleImportEqualsDeclaration, - isForInitializer, - isGeneratedIdentifier, - isHeritageClause, - isIdentifier, - isImportCall, - isImportClause, - isImportMeta, - isImportSpecifier, - isLocalName, - isModifierLike, - isNamedExports, - isObjectLiteralExpression, - isOmittedExpression, - isParameter, - isPrefixUnaryExpression, - isPropertyAssignment, - isShorthandPropertyAssignment, - isSpreadElement, - isStatement, - isStringLiteral, - isVariableDeclarationList, LabeledStatement, - map, MetaProperty, ModifierFlags, - moveEmitHelpers, Node, NodeFlags, ObjectLiteralElementLike, - outFile, ParenthesizedExpression, PartiallyEmittedExpression, PostfixUnaryExpression, PrefixUnaryExpression, PropertyAssignment, - setCommentRange, - setEmitFlags, - setTextRange, ShorthandPropertyAssignment, - singleOrMany, - some, SourceFile, - startOnNewLine, Statement, StringLiteral, SwitchStatement, @@ -114,19 +91,60 @@ import { TextRange, TransformationContext, TransformFlags, - tryGetModuleNameFromFile, TryStatement, VariableDeclaration, VariableDeclarationList, VariableStatement, + VisitResult, + WhileStatement, + WithStatement, +} from "../../types"; +import { + getEmitFlags, + getNodeId, + getStrictOptionValue, + getTextOfIdentifierOrLiteral, + hasSyntacticModifier, + insertStatementsAfterStandardPrologue, + isAssignmentExpression, + isAssignmentOperator, + isDeclarationNameOfEnumOrNamespace, + isDestructuringAssignment, + isEffectiveExternalModule, + isExternalModule, + isExternalModuleImportEqualsDeclaration, + isImportCall, + isImportMeta, + outFile, +} from "../../utilities"; +import { + getOriginalNode, + idText, + isBindingPattern, + isCaseOrDefaultClause, + isClassElement, + isExpression, + isForInitializer, + isGeneratedIdentifier, + isModifierLike, + isStatement, +} from "../../utilitiesPublic"; +import { visitEachChild, visitIterationBody, visitNode, visitNodes, - VisitResult, - WhileStatement, - WithStatement, -} from "../../_namespaces/ts"; +} from "../../visitorPublic"; +import { + flattenDestructuringAssignment, + FlattenLevel, +} from "../destructuring"; +import { + chainBundle, + collectExternalModuleInfo, + ExternalModuleInfo, + getOriginalNodeId, +} from "../utilities"; /** @internal */ export function transformSystemModule(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { diff --git a/src/compiler/transformers/taggedTemplate.ts b/src/compiler/transformers/taggedTemplate.ts index ca23140c0d448..681618cea6a00 100644 --- a/src/compiler/transformers/taggedTemplate.ts +++ b/src/compiler/transformers/taggedTemplate.ts @@ -1,16 +1,12 @@ +import * as Debug from "../debug"; +import { isNoSubstitutionTemplateLiteral } from "../factory/nodeTests"; +import { setTextRange } from "../factory/utilitiesPublic"; import { CallExpression, - Debug, Expression, - getSourceTextOfNodeFromSourceFile, - hasInvalidEscape, Identifier, - isExpression, - isExternalModule, - isNoSubstitutionTemplateLiteral, NodeFactory, NoSubstitutionTemplateLiteral, - setTextRange, SourceFile, SyntaxKind, TaggedTemplateExpression, @@ -20,10 +16,18 @@ import { TemplateTail, TokenFlags, TransformationContext, + Visitor, +} from "../types"; +import { + getSourceTextOfNodeFromSourceFile, + hasInvalidEscape, + isExternalModule, +} from "../utilities"; +import { isExpression } from "../utilitiesPublic"; +import { visitEachChild, visitNode, - Visitor, -} from "../_namespaces/ts"; +} from "../visitorPublic"; /** @internal */ export enum ProcessLevel { diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 6fec0b61bd294..a908110ac28f5 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1,32 +1,75 @@ +import { isInstantiatedModule } from "../checker"; +import { + addRange, + append, + concatenate, + filter, + isArray, + map, + mapDefined, + skipWhile, + some, + takeWhile, +} from "../core"; +import * as Debug from "../debug"; import { - __String, - AccessorDeclaration, addEmitFlags, addEmitHelpers, - addRange, addSyntheticTrailingComment, - append, + removeAllComments, + setCommentRange, + setConstantValue, + setEmitFlags, + setInternalEmitFlags, + setSourceMapRange, + setSyntheticLeadingComments, + setSyntheticTrailingComments, + setTypeNode, +} from "../factory/emitNode"; +import { createUnparsedSourceFile } from "../factory/nodeFactory"; +import { + isComputedPropertyName, + isDecorator, + isElementAccessExpression, + isExportSpecifier, + isHeritageClause, + isIdentifier, + isImportClause, + isImportSpecifier, + isJsxAttributes, + isModuleDeclaration, + isNamespaceExport, + isPrivateIdentifier, + isPropertyAccessExpression, + isShorthandPropertyAssignment, + isSourceFile, +} from "../factory/nodeTests"; +import { + createExpressionFromEntityName, + isExportOrDefaultModifier, + isLocalName, + startOnNewLine, +} from "../factory/utilities"; +import { + setOriginalNode, + setTextRange, +} from "../factory/utilitiesPublic"; +import { setParent } from "../parserUtilities"; +import { skipTrivia } from "../scanner"; +import { + __String, + AccessorDeclaration, ArrowFunction, AssertionExpression, Block, Bundle, CallExpression, CaseBlock, - childIsDecorated, ClassDeclaration, ClassElement, - classElementOrClassElementParameterIsDecorated, ClassExpression, ClassLikeDeclaration, - classOrConstructorParameterIsDecorated, - concatenate, ConstructorDeclaration, - createExpressionFromEntityName, - createRange, - createRuntimeTypeSerializer, - createTokenRange, - createUnparsedSourceFile, - Debug, Declaration, Decorator, ElementAccessExpression, @@ -40,99 +83,28 @@ import { ExportSpecifier, Expression, ExpressionWithTypeArguments, - filter, - findSuperStatementIndex, - flattenDestructuringAssignment, - FlattenLevel, FunctionDeclaration, FunctionExpression, FunctionLikeDeclaration, GetAccessorDeclaration, - getEffectiveBaseTypeNode, - getEmitFlags, - getEmitModuleKind, - getEmitScriptTarget, - getFirstConstructorWithBody, - getInitializedVariables, - getIsolatedModules, - getOriginalNode, - getParseTreeNode, - getProperties, - getStrictOptionValue, - getTextOfNode, - hasDecorators, - hasStaticModifier, - hasSyntacticModifier, HeritageClause, Identifier, - idText, ImportClause, ImportDeclaration, ImportEqualsDeclaration, ImportsNotUsedAsValues, ImportSpecifier, InitializedVariableDeclaration, - insertStatementsAfterStandardPrologue, InternalEmitFlags, - isAccessExpression, - isArray, - isAssertionExpression, - isBindingName, - isBindingPattern, - isClassElement, - isClassLike, - isComputedPropertyName, - isDecorator, - isElementAccessExpression, - isEnumConst, - isExportOrDefaultModifier, - isExportSpecifier, - isExpression, - isExternalModule, - isExternalModuleImportEqualsDeclaration, - isGeneratedIdentifier, - isHeritageClause, - isIdentifier, - isImportClause, - isImportSpecifier, - isInJSFile, - isInstantiatedModule, - isJsonSourceFile, - isJsxAttributes, - isJsxTagNameExpression, - isLeftHandSideExpression, - isLocalName, - isModifier, - isModifierLike, - isModuleDeclaration, - isNamedExportBindings, - isNamedImportBindings, - isNamespaceExport, - isObjectLiteralElementLike, - isParameterPropertyDeclaration, - isPrivateIdentifier, - isPropertyAccessExpression, - isPropertyName, - isShorthandPropertyAssignment, - isSimpleInlineableExpression, - isSourceFile, - isStatement, - isTemplateLiteral, JsxOpeningElement, JsxSelfClosingElement, LeftHandSideExpression, - map, - mapDefined, MethodDeclaration, ModifierFlags, ModifierLike, - modifierToFlag, ModuleBlock, ModuleDeclaration, ModuleKind, - moveRangePastDecorators, - moveRangePastModifiers, - moveRangePos, NamedExportBindings, NamedExports, NamedImportBindings, @@ -141,61 +113,109 @@ import { Node, NodeArray, NodeFlags, - nodeIsMissing, NonNullExpression, ObjectLiteralElementLike, ObjectLiteralExpression, OuterExpressionKinds, ParameterDeclaration, - parameterIsThisKeyword, - ParameterPropertyDeclaration, ParenthesizedExpression, PropertyAccessExpression, PropertyDeclaration, PropertyName, - removeAllComments, SatisfiesExpression, ScriptTarget, SetAccessorDeclaration, - setCommentRange, - setConstantValue, - setEmitFlags, - setInternalEmitFlags, - setOriginalNode, - setParent, - setSourceMapRange, - setSyntheticLeadingComments, - setSyntheticTrailingComments, - setTextRange, - setTextRangeEnd, - setTextRangePos, - setTypeNode, ShorthandPropertyAssignment, - shouldPreserveConstEnums, - skipOuterExpressions, - skipPartiallyEmittedExpressions, - skipTrivia, - skipWhile, - some, SourceFile, - startOnNewLine, Statement, SyntaxKind, TaggedTemplateExpression, - takeWhile, TextRange, TransformationContext, TransformFlags, VariableDeclaration, VariableStatement, + VisitResult, +} from "../types"; +import { + childIsDecorated, + classElementOrClassElementParameterIsDecorated, + classOrConstructorParameterIsDecorated, + createRange, + createTokenRange, + getEffectiveBaseTypeNode, + getEmitFlags, + getEmitModuleKind, + getEmitScriptTarget, + getFirstConstructorWithBody, + getInitializedVariables, + getIsolatedModules, + getStrictOptionValue, + getTextOfNode, + hasDecorators, + hasStaticModifier, + hasSyntacticModifier, + insertStatementsAfterStandardPrologue, + isAccessExpression, + isEnumConst, + isExternalModule, + isExternalModuleImportEqualsDeclaration, + isInJSFile, + isJsonSourceFile, + modifierToFlag, + moveRangePastDecorators, + moveRangePastModifiers, + moveRangePos, + nodeIsMissing, + parameterIsThisKeyword, + setTextRangeEnd, + setTextRangePos, + shouldPreserveConstEnums, + skipOuterExpressions, +} from "../utilities"; +import { + getOriginalNode, + getParseTreeNode, + idText, + isAssertionExpression, + isBindingName, + isBindingPattern, + isClassElement, + isClassLike, + isExpression, + isGeneratedIdentifier, + isJsxTagNameExpression, + isLeftHandSideExpression, + isModifier, + isModifierLike, + isNamedExportBindings, + isNamedImportBindings, + isObjectLiteralElementLike, + isParameterPropertyDeclaration, + isPropertyName, + isStatement, + isTemplateLiteral, + ParameterPropertyDeclaration, + skipPartiallyEmittedExpressions, +} from "../utilitiesPublic"; +import { visitEachChild, visitFunctionBody, visitLexicalEnvironment, visitNode, visitNodes, visitParameterList, - VisitResult, -} from "../_namespaces/ts"; +} from "../visitorPublic"; +import { + flattenDestructuringAssignment, + FlattenLevel, +} from "./destructuring"; +import { createRuntimeTypeSerializer } from "./typeSerializer"; +import { + findSuperStatementIndex, + getProperties, + isSimpleInlineableExpression, +} from "./utilities"; /** * Indicates whether to emit type metadata in the new format. diff --git a/src/compiler/transformers/typeSerializer.ts b/src/compiler/transformers/typeSerializer.ts index 831a581b8258b..929713cebb248 100644 --- a/src/compiler/transformers/typeSerializer.ts +++ b/src/compiler/transformers/typeSerializer.ts @@ -1,3 +1,20 @@ +import * as Debug from "../debug"; +import { + isBinaryExpression, + isConditionalExpression, + isConditionalTypeNode, + isIdentifier, + isLiteralTypeNode, + isNumericLiteral, + isParenthesizedExpression, + isPropertyAccessExpression, + isStringLiteral, + isTypeOfExpression, + isVoidExpression, +} from "../factory/nodeTests"; +import { setTextRange } from "../factory/utilitiesPublic"; +import { parseNodeFactory } from "../parser"; +import { setParent } from "../parserUtilities"; import { AccessorDeclaration, ArrayLiteralExpression, @@ -8,35 +25,10 @@ import { ClassLikeDeclaration, ConditionalExpression, ConditionalTypeNode, - Debug, EntityName, Expression, - findAncestor, FunctionLikeDeclaration, - getAllAccessorDeclarations, - getEffectiveReturnTypeNode, - getEmitScriptTarget, - getFirstConstructorWithBody, - getParseTreeNode, - getRestParameterElementType, - getSetAccessorTypeAnnotationNode, - getStrictOptionValue, Identifier, - isAsyncFunction, - isBinaryExpression, - isClassLike, - isConditionalExpression, - isConditionalTypeNode, - isFunctionLike, - isGeneratedIdentifier, - isIdentifier, - isLiteralTypeNode, - isNumericLiteral, - isParenthesizedExpression, - isPropertyAccessExpression, - isStringLiteral, - isTypeOfExpression, - isVoidExpression, JSDocNonNullableType, JSDocNullableType, JSDocOptionalType, @@ -44,19 +36,14 @@ import { MethodDeclaration, ModuleBlock, Node, - nodeIsPresent, NumericLiteral, ParameterDeclaration, - parseNodeFactory, PrefixUnaryExpression, PropertyAccessEntityNameExpression, PropertyDeclaration, QualifiedName, ScriptTarget, - setParent, - setTextRange, SignatureDeclaration, - skipTypeParentheses, SourceFile, SyntaxKind, TransformationContext, @@ -67,7 +54,26 @@ import { TypeReferenceSerializationKind, UnionOrIntersectionTypeNode, VoidExpression, -} from "../_namespaces/ts"; +} from "../types"; +import { + getAllAccessorDeclarations, + getEffectiveReturnTypeNode, + getEmitScriptTarget, + getFirstConstructorWithBody, + getRestParameterElementType, + getSetAccessorTypeAnnotationNode, + getStrictOptionValue, + isAsyncFunction, + nodeIsPresent, + skipTypeParentheses, +} from "../utilities"; +import { + findAncestor, + getParseTreeNode, + isClassLike, + isFunctionLike, + isGeneratedIdentifier, +} from "../utilitiesPublic"; /** @internal */ export type SerializedEntityName = diff --git a/src/compiler/transformers/utilities.ts b/src/compiler/transformers/utilities.ts index c094a8dc0e76c..e873b00ac3a8b 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -1,12 +1,31 @@ +import { + append, + cast, + createMultiMap, + filter, + map, + some, +} from "../core"; +import { + isClassStaticBlockDeclaration, + isExpressionStatement, + isIdentifier, + isNamedExports, + isNamedImports, + isOmittedExpression, + isPrivateIdentifier, + isPropertyDeclaration, +} from "../factory/nodeTests"; +import { + createExternalHelpersImportDeclarationIfNeeded, getNodeForGeneratedName, isLocalName, +} from "../factory/utilities"; import { __String, AccessorDeclaration, AllDecorators, - append, BinaryOperator, BindingElement, Bundle, - cast, ClassDeclaration, ClassElement, ClassExpression, @@ -15,69 +34,34 @@ import { CompilerOptions, CompoundAssignmentOperator, CoreTransformationContext, - createExternalHelpersImportDeclarationIfNeeded, - createMultiMap, Decorator, EmitResolver, ExportAssignment, ExportDeclaration, ExportSpecifier, Expression, - filter, FunctionDeclaration, FunctionLikeDeclaration, - getAllAccessorDeclarations, - getDecorators, - getFirstConstructorWithBody, - getNamespaceDeclarationNode, - getNodeForGeneratedName, - getNodeId, - getOriginalNode, - hasDecorators, - hasStaticModifier, - hasSyntacticModifier, Identifier, - idText, ImportDeclaration, ImportEqualsDeclaration, ImportSpecifier, InitializedPropertyDeclaration, InternalSymbolName, - isAutoAccessorPropertyDeclaration, - isBindingPattern, - isClassStaticBlockDeclaration, - isDefaultImport, - isExpressionStatement, - isGeneratedIdentifier, - isGeneratedPrivateIdentifier, - isIdentifier, - isKeyword, - isLocalName, - isMethodOrAccessor, - isNamedExports, - isNamedImports, - isOmittedExpression, - isPrivateIdentifier, - isPropertyDeclaration, - isStatic, - isStringLiteralLike, - isSuperCall, LogicalOperatorOrHigher, - map, MethodDeclaration, ModifierFlags, + ModuleKind, NamedImportBindings, NamespaceExport, Node, NodeArray, - parameterIsThisKeyword, PrivateIdentifier, PrivateIdentifierAccessorDeclaration, PrivateIdentifierAutoAccessorPropertyDeclaration, PrivateIdentifierMethodDeclaration, PropertyDeclaration, - skipParentheses, - some, + ScriptKind, SourceFile, Statement, SuperCall, @@ -85,7 +69,38 @@ import { TransformationContext, VariableDeclaration, VariableStatement, -} from "../_namespaces/ts"; +} from "../types"; +import { + getAllAccessorDeclarations, + getEmitModuleKind, + getFirstConstructorWithBody, + getIsolatedModules, + getNamespaceDeclarationNode, + getNodeId, + getStrictOptionValue, + hasDecorators, + hasStaticModifier, + hasSyntacticModifier, + isDefaultImport, + isExternalModule, + isKeyword, + isStatic, + isSuperCall, + parameterIsThisKeyword, + skipParentheses, + startsWithUseStrict, +} from "../utilities"; +import { + getDecorators, + getOriginalNode, + idText, + isAutoAccessorPropertyDeclaration, + isBindingPattern, + isGeneratedIdentifier, + isGeneratedPrivateIdentifier, + isMethodOrAccessor, + isStringLiteralLike, +} from "../utilitiesPublic"; /** @internal */ export function getOriginalNodeId(node: Node) { @@ -725,3 +740,42 @@ export function accessPrivateIdentifier< ) { return walkUpLexicalEnvironments(env, env => getPrivateIdentifier(env.privateEnv, name)); } + +/** + * Returns whether the source file will be treated as if it were in strict mode at runtime. + * + * @internal + */ +export function isEffectiveStrictModeSourceFile(node: SourceFile, compilerOptions: CompilerOptions) { + // We can only verify strict mode for JS/TS files + switch (node.scriptKind) { + case ScriptKind.JS: + case ScriptKind.TS: + case ScriptKind.JSX: + case ScriptKind.TSX: + break; + default: + return false; + } + // Strict mode does not matter for declaration files. + if (node.isDeclarationFile) { + return false; + } + // If `alwaysStrict` is set, then treat the file as strict. + if (getStrictOptionValue(compilerOptions, "alwaysStrict")) { + return true; + } + // Starting with a "use strict" directive indicates the file is strict. + if (startsWithUseStrict(node.statements)) { + return true; + } + if (isExternalModule(node) || getIsolatedModules(compilerOptions)) { + // ECMAScript Modules are always strict. + if (getEmitModuleKind(compilerOptions) >= ModuleKind.ES2015) { + return true; + } + // Other modules are strict unless otherwise specified. + return !compilerOptions.noImplicitUseStrict; + } + return false; +} diff --git a/src/compiler/tsbuild.ts b/src/compiler/tsbuild.ts index 741ffe0a8941d..bbe79bcf331ed 100644 --- a/src/compiler/tsbuild.ts +++ b/src/compiler/tsbuild.ts @@ -1,10 +1,12 @@ import { combinePaths, - Extension, fileExtensionIs, +} from "./path"; +import { + Extension, Path, ResolvedConfigFileName, -} from "./_namespaces/ts"; +} from "./types"; /** @internal */ export enum UpToDateStatusType { diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 3bc998eb7dc26..b659525c498d8 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -1,135 +1,163 @@ +import { + getBuildInfoFileVersionMap, + getPendingEmitKind, + ProgramBuildInfo, + ProgramBundleEmitBuildInfo, + ProgramMultiFileEmitBuildInfo, +} from "./builder"; import { AffectedFileResult, - arrayToMap, - assertType, BuilderProgram, - BuildInfo, - CancellationToken, + EmitAndSemanticDiagnosticsBuilderProgram, + SemanticDiagnosticsBuilderProgram, +} from "./builderPublic"; +import { OutputFile } from "./builderStatePublic"; +import { canJsonReportNoInputFiles, - changeCompilerHostLikeToUseCache, - clearMap, - closeFileWatcher, - closeFileWatcherOf, commonOptionsWithBuild, - CompilerHost, - CompilerOptions, - CompilerOptionsValue, - ConfigFileProgramReloadLevel, - convertToRelativePath, + DiagnosticReporter, + ExtendedConfigCacheEntry, + getFileNamesFromConfigSpecs, + getParsedCommandLineOfConfigFile, + ParseConfigFileHost, + updateErrorForNoInputFiles, +} from "./commandLineParser"; +import { + arrayToMap, + assertType, copyProperties, - createCompilerDiagnostic, - createCompilerHostFromProgramHost, - createDiagnosticCollection, - createDiagnosticReporter, + emptyArray, + findIndex, + forEach, + hasProperty, + identity, + isArray, + isString, + map, + maybeBind, + noop, + returnUndefined, + some, + unorderedRemoveItem, +} from "./core"; +import { version } from "./corePublic"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { + emitUsingBuildInfo, + getAllProjectOutputs, + getBuildInfo, + getFirstProjectOutput, + getTsBuildInfoEmitOutputFilePath, +} from "./emitter"; +import { createModuleResolutionCache, - createModuleResolutionLoader, - CreateProgram, - createProgramHost, createTypeReferenceDirectiveResolutionCache, + ModuleResolutionCache, + resolveLibrary, + TypeReferenceDirectiveResolutionCache, +} from "./moduleNameResolver"; +import { + convertToRelativePath, + getDirectoryPath, + getNormalizedAbsolutePath, + resolvePath, + toPath, +} from "./path"; +import * as performance from "./performance"; +import { + changeCompilerHostLikeToUseCache, + createModuleResolutionLoader, createTypeReferenceResolutionLoader, - createWatchFactory, - createWatchHost, + flattenDiagnosticMessageText, + ForegroundColorEscapeSequences, + formatColorAndReset, + getConfigFileParsingDiagnostics, + loadWithModeAwareCache, + parseConfigHostFromCompilerHostLike, + resolveProjectReferencePath, +} from "./program"; +import { + getModifiedTime, + missingFileModifiedTime, + PollingInterval, + sys, +} from "./sys"; +import { + resolveConfigFileProjectName, + Status, + UpToDateStatus, + UpToDateStatusType, +} from "./tsbuild"; +import { + BuildInfo, + CancellationToken, + CompilerHost, + CompilerOptions, + CompilerOptionsValue, CustomTransformers, - Debug, Diagnostic, DiagnosticArguments, DiagnosticCollection, DiagnosticMessage, - DiagnosticReporter, - Diagnostics, - EmitAndSemanticDiagnosticsBuilderProgram, - emitFilesAndReportErrors, EmitResult, - emitUsingBuildInfo, - emptyArray, ExitStatus, - ExtendedConfigCacheEntry, FileWatcher, FileWatcherCallback, - findIndex, - flattenDiagnosticMessageText, - forEach, - ForegroundColorEscapeSequences, - formatColorAndReset, - getAllProjectOutputs, - getBuildInfo as ts_getBuildInfo, - getBuildInfoFileVersionMap, - getConfigFileParsingDiagnostics, - getDirectoryPath, + ParsedCommandLine, + Path, + Program, + ResolvedConfigFileName, + SourceFile, + System, + WatchOptions, + WriteFileCallback, +} from "./types"; +import { + clearMap, + closeFileWatcher, + createCompilerDiagnostic, + createDiagnosticCollection, + isIncrementalCompilation, + mutateMap, + mutateMapSkippingNewValues, + outFile, + writeFile, +} from "./utilities"; +import { + createCompilerHostFromProgramHost, + createDiagnosticReporter, + createProgramHost, + createWatchFactory, + createWatchHost, + emitFilesAndReportErrors, getErrorCountForSummary, - getFileNamesFromConfigSpecs, getFilesInErrorForSummary, - getFirstProjectOutput, getLocaleTimeString, - getModifiedTime as ts_getModifiedTime, - getNormalizedAbsolutePath, - getParsedCommandLineOfConfigFile, - getPendingEmitKind, getSourceFileVersionAsHashFromText, - getTsBuildInfoEmitOutputFilePath, getWatchErrorSummaryDiagnosticMessage, - hasProperty, - identity, - isArray, - isIgnoredFileFromWildCardWatching, - isIncrementalCompilation, - isString, listFiles, - loadWithModeAwareCache, - map, - maybeBind, - missingFileModifiedTime, - ModuleResolutionCache, - mutateMap, - mutateMapSkippingNewValues, - noop, - outFile, - OutputFile, - ParseConfigFileHost, - parseConfigHostFromCompilerHostLike, - ParsedCommandLine, - Path, - PollingInterval, - Program, - ProgramBuildInfo, - ProgramBundleEmitBuildInfo, + setGetSourceFileAsHashVersioned, + WatchType, +} from "./watch"; +import { + CreateProgram, ProgramHost, - ProgramMultiFileEmitBuildInfo, readBuilderProgram, ReadBuildProgramHost, - resolveConfigFileProjectName, - ResolvedConfigFileName, - resolveLibrary, - resolvePath, - resolveProjectReferencePath, - returnUndefined, - SemanticDiagnosticsBuilderProgram, - setGetSourceFileAsHashVersioned, + WatchHost, + WatchStatusReporter, +} from "./watchPublic"; +import { + closeFileWatcherOf, + ConfigFileProgramReloadLevel, + isIgnoredFileFromWildCardWatching, SharedExtendedConfigFileWatcher, - some, - SourceFile, - Status, - sys, - System, - toPath as ts_toPath, - TypeReferenceDirectiveResolutionCache, - unorderedRemoveItem, - updateErrorForNoInputFiles, updateSharedExtendedConfigFileWatcher, updateWatchingWildcardDirectories, - UpToDateStatus, - UpToDateStatusType, - version, WatchFactory, - WatchHost, - WatchOptions, - WatchStatusReporter, - WatchType, WildcardDirectoryWatcher, - writeFile, - WriteFileCallback, -} from "./_namespaces/ts"; -import * as performance from "./_namespaces/ts.performance"; +} from "./watchUtilities"; const minimumDate = new Date(-8640000000000000); const maximumDate = new Date(8640000000000000); @@ -479,7 +507,7 @@ function createSolutionBuilderState(watch: boolean, ho libraryResolutionCache, ); } - compilerHost.getBuildInfo = (fileName, configFilePath) => getBuildInfo(state, fileName, toResolvedConfigFilePath(state, configFilePath as ResolvedConfigFileName), /*modifiedTime*/ undefined); + compilerHost.getBuildInfo = (fileName, configFilePath) => getBuildInfoWorker(state, fileName, toResolvedConfigFilePath(state, configFilePath as ResolvedConfigFileName), /*modifiedTime*/ undefined); const { watchFile, watchDirectory, writeLog } = createWatchFactory(hostWithWatch, options); @@ -542,8 +570,8 @@ function createSolutionBuilderState(watch: boolean, ho return state; } -function toPath(state: SolutionBuilderState, fileName: string) { - return ts_toPath(fileName, state.compilerHost.getCurrentDirectory(), state.compilerHost.getCanonicalFileName); +function toPathWorker(state: SolutionBuilderState, fileName: string) { + return toPath(fileName, state.compilerHost.getCurrentDirectory(), state.compilerHost.getCanonicalFileName); } function toResolvedConfigFilePath(state: SolutionBuilderState, fileName: ResolvedConfigFileName): ResolvedConfigFilePath { @@ -551,7 +579,7 @@ function toResolvedConfigFilePath(state: SolutionBuild const path = resolvedConfigFilePaths.get(fileName); if (path !== undefined) return path; - const resolvedPath = toPath(state, fileName) as ResolvedConfigFilePath; + const resolvedPath = toPathWorker(state, fileName) as ResolvedConfigFilePath; resolvedConfigFilePaths.set(fileName, resolvedPath); return resolvedPath; } @@ -742,7 +770,7 @@ function enableCache(state: SolutionBuilderState) { getSourceFileWithCache, readFileWithCache } = changeCompilerHostLikeToUseCache( host, - fileName => toPath(state, fileName), + fileName => toPathWorker(state, fileName), (...args) => originalGetSourceFile.call(compilerHost, ...args) ); state.readFileWithCache = readFileWithCache; @@ -1073,7 +1101,7 @@ function createBuildOrUpdateInvalidedProject( if (state.watch) { state.lastCachedPackageJsonLookups.set(projectPath, state.moduleResolutionCache && map( state.moduleResolutionCache.getPackageJsonInfoCache().entries(), - ([path, data]) => ([state.host.realpath && data ? toPath(state, state.host.realpath(path)) : path, data] as const) + ([path, data]) => ([state.host.realpath && data ? toPathWorker(state, state.host.realpath(path)) : path, data] as const) )); state.builderPrograms.set(projectPath, program); @@ -1166,10 +1194,10 @@ function createBuildOrUpdateInvalidedProject( let outputTimeStampMap: Map | undefined; let now: Date | undefined; outputFiles.forEach(({ name, text, writeByteOrderMark, data }) => { - const path = toPath(state, name); - emittedOutputs.set(toPath(state, name), name); + const path = toPathWorker(state, name); + emittedOutputs.set(toPathWorker(state, name), name); if (data?.buildInfo) setBuildInfo(state, data.buildInfo, projectPath, options, resultFlags); - const modifiedTime = data?.differsOnlyInMap ? ts_getModifiedTime(state.host, name) : undefined; + const modifiedTime = data?.differsOnlyInMap ? getModifiedTime(state.host, name) : undefined; writeFile(writeFileCallback ? { writeFile: writeFileCallback } : compilerHost, emitterDiagnostics, name, text, writeByteOrderMark); // Revert the timestamp for the d.ts that is same if (data?.differsOnlyInMap) state.host.setModifiedTime(name, modifiedTime!); @@ -1292,7 +1320,7 @@ function createBuildOrUpdateInvalidedProject( let resultFlags = BuildResultFlags.DeclarationOutputUnchanged; const existingBuildInfo = state.buildInfoCache.get(projectPath)!.buildInfo || undefined; outputFiles.forEach(({ name, text, writeByteOrderMark, data }) => { - emittedOutputs.set(toPath(state, name), name); + emittedOutputs.set(toPathWorker(state, name), name); if (data?.buildInfo) { if ((data.buildInfo.program as ProgramBundleEmitBuildInfo)?.outSignature !== (existingBuildInfo?.program as ProgramBundleEmitBuildInfo)?.outSignature) { resultFlags &= ~BuildResultFlags.DeclarationOutputUnchanged; @@ -1574,8 +1602,8 @@ function isFileWatcherWithModifiedTime(value: FileWatcherWithModifiedTime | Date return !!(value as FileWatcherWithModifiedTime).watcher; } -function getModifiedTime(state: SolutionBuilderState, fileName: string): Date { - const path = toPath(state, fileName); +function getModifiedTimeWorker(state: SolutionBuilderState, fileName: string): Date { + const path = toPathWorker(state, fileName); const existing = state.filesWatched.get(path); if (state.watch && !!existing) { if (!isFileWatcherWithModifiedTime(existing)) return existing; @@ -1584,7 +1612,7 @@ function getModifiedTime(state: SolutionBuilderState(state: SolutionBuilderState(state: SolutionBuilderState, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, watchType: WatchType, project?: ResolvedConfigFileName): FileWatcher { - const path = toPath(state, file); + const path = toPathWorker(state, file); const existing = state.filesWatched.get(path); if (existing && isFileWatcherWithModifiedTime(existing)) { existing.callbacks.push(callback); @@ -1655,7 +1683,7 @@ function setBuildInfo( } else { state.buildInfoCache.set(resolvedConfigPath, { - path: toPath(state, buildInfoPath), + path: toPathWorker(state, buildInfoPath), buildInfo, modifiedTime, latestChangedDtsTime: resultFlags & BuildResultFlags.DeclarationOutputUnchanged ? undefined : modifiedTime, @@ -1664,26 +1692,26 @@ function setBuildInfo( } function getBuildInfoCacheEntry(state: SolutionBuilderState, buildInfoPath: string, resolvedConfigPath: ResolvedConfigFilePath) { - const path = toPath(state, buildInfoPath); + const path = toPathWorker(state, buildInfoPath); const existing = state.buildInfoCache.get(resolvedConfigPath); return existing?.path === path ? existing : undefined; } -function getBuildInfo(state: SolutionBuilderState, buildInfoPath: string, resolvedConfigPath: ResolvedConfigFilePath, modifiedTime: Date | undefined): BuildInfo | undefined { - const path = toPath(state, buildInfoPath); +function getBuildInfoWorker(state: SolutionBuilderState, buildInfoPath: string, resolvedConfigPath: ResolvedConfigFilePath, modifiedTime: Date | undefined): BuildInfo | undefined { + const path = toPathWorker(state, buildInfoPath); const existing = state.buildInfoCache.get(resolvedConfigPath); if (existing !== undefined && existing.path === path) { return existing.buildInfo || undefined; } const value = state.readFileWithCache(buildInfoPath); - const buildInfo = value ? ts_getBuildInfo(buildInfoPath, value) : undefined; + const buildInfo = value ? getBuildInfo(buildInfoPath, value) : undefined; state.buildInfoCache.set(resolvedConfigPath, { path, buildInfo: buildInfo || false, modifiedTime: modifiedTime || missingFileModifiedTime }); return buildInfo; } function checkConfigFileUpToDateStatus(state: SolutionBuilderState, configFile: string, oldestOutputFileTime: Date, oldestOutputFileName: string): Status.OutOfDateWithSelf | undefined { // Check tsconfig time - const tsconfigTime = getModifiedTime(state, configFile); + const tsconfigTime = getModifiedTimeWorker(state, configFile); if (oldestOutputFileTime < tsconfigTime) { return { type: UpToDateStatusType.OutOfDateWithSelf, @@ -1751,11 +1779,11 @@ function getUpToDateStatusWorker(state: SolutionBuilde let buildInfoVersionMap: ReturnType | undefined; if (buildInfoPath) { const buildInfoCacheEntry = getBuildInfoCacheEntry(state, buildInfoPath, resolvedPath); - buildInfoTime = buildInfoCacheEntry?.modifiedTime || ts_getModifiedTime(host, buildInfoPath); + buildInfoTime = buildInfoCacheEntry?.modifiedTime || getModifiedTime(host, buildInfoPath); if (buildInfoTime === missingFileModifiedTime) { if (!buildInfoCacheEntry) { state.buildInfoCache.set(resolvedPath, { - path: toPath(state, buildInfoPath), + path: toPathWorker(state, buildInfoPath), buildInfo: false, modifiedTime: buildInfoTime }); @@ -1766,7 +1794,7 @@ function getUpToDateStatusWorker(state: SolutionBuilde }; } - const buildInfo = getBuildInfo(state, buildInfoPath, resolvedPath, buildInfoTime); + const buildInfo = getBuildInfoWorker(state, buildInfoPath, resolvedPath, buildInfoTime); if (!buildInfo) { // Error reading buildInfo return { @@ -1820,7 +1848,7 @@ function getUpToDateStatusWorker(state: SolutionBuilde const seenRoots = new Set(); // Get timestamps of input files for (const inputFile of project.fileNames) { - const inputTime = getModifiedTime(state, inputFile); + const inputTime = getModifiedTimeWorker(state, inputFile); if (inputTime === missingFileModifiedTime) { return { type: UpToDateStatusType.Unbuildable, @@ -1835,7 +1863,7 @@ function getUpToDateStatusWorker(state: SolutionBuilde if (buildInfoProgram) { // Read files and see if they are same, read is anyways cached if (!buildInfoVersionMap) buildInfoVersionMap = getBuildInfoFileVersionMap(buildInfoProgram, buildInfoPath!, host); - version = buildInfoVersionMap.fileInfos.get(toPath(state, inputFile)); + version = buildInfoVersionMap.fileInfos.get(toPathWorker(state, inputFile)); const text = version ? state.readFileWithCache(inputFile) : undefined; currentVersion = text !== undefined ? getSourceFileVersionAsHashFromText(host, text) : undefined; if (version && version === currentVersion) pseudoInputUpToDate = true; @@ -1855,7 +1883,7 @@ function getUpToDateStatusWorker(state: SolutionBuilde newestInputFileTime = inputTime; } - if (buildInfoProgram) seenRoots.add(toPath(state, inputFile)); + if (buildInfoProgram) seenRoots.add(toPathWorker(state, inputFile)); } if (buildInfoProgram) { @@ -1879,11 +1907,11 @@ function getUpToDateStatusWorker(state: SolutionBuilde const outputs = getAllProjectOutputs(project, !host.useCaseSensitiveFileNames()); const outputTimeStampMap = getOutputTimeStampMap(state, resolvedPath); for (const output of outputs) { - const path = toPath(state, output); + const path = toPathWorker(state, output); // Output is missing; can stop checking let outputTime = outputTimeStampMap?.get(path); if (!outputTime) { - outputTime = ts_getModifiedTime(state.host, output); + outputTime = getModifiedTime(state.host, output); outputTimeStampMap?.set(path, outputTime); } @@ -2026,7 +2054,7 @@ function updateOutputTimestampsWorker( if (buildInfoPath) { // For incremental projects, only buildinfo needs to be upto date with timestamp check // as we dont check output files for up-to-date ness - if (!skipOutputs?.has(toPath(state, buildInfoPath))) { + if (!skipOutputs?.has(toPathWorker(state, buildInfoPath))) { if (!!state.options.verbose) reportStatus(state, verboseMessage, proj.options.configFilePath!); state.host.setModifiedTime(buildInfoPath, now = getCurrentTime(state.host)); getBuildInfoCacheEntry(state, buildInfoPath, projectPath)!.modifiedTime = now; @@ -2042,7 +2070,7 @@ function updateOutputTimestampsWorker( if (!skipOutputs || outputs.length !== skipOutputs.size) { let reportVerbose = !!state.options.verbose; for (const file of outputs) { - const path = toPath(state, file); + const path = toPathWorker(state, file); if (skipOutputs?.has(path)) continue; if (reportVerbose) { reportVerbose = false; @@ -2222,10 +2250,10 @@ function cleanWorker(state: SolutionBuilderState, p } const outputs = getAllProjectOutputs(parsed, !host.useCaseSensitiveFileNames()); if (!outputs.length) continue; - const inputFileNames = new Set(parsed.fileNames.map(f => toPath(state, f))); + const inputFileNames = new Set(parsed.fileNames.map(f => toPathWorker(state, f))); for (const output of outputs) { // If output name is same as input file name, do not delete and ignore the error - if (inputFileNames.has(toPath(state, output))) continue; + if (inputFileNames.has(toPathWorker(state, output))) continue; if (host.fileExists(output)) { if (filesToDelete) { filesToDelete.push(output); @@ -2345,7 +2373,7 @@ function watchExtendedConfigFiles(state: SolutionBuild parsed?.watchOptions, WatchType.ExtendedConfigFile, ), - fileName => toPath(state, fileName), + fileName => toPathWorker(state, fileName), ); } @@ -2358,16 +2386,16 @@ function watchWildCardDirectories(state: SolutionBuild dir, fileOrDirectory => { if (isIgnoredFileFromWildCardWatching({ - watchedDirPath: toPath(state, dir), + watchedDirPath: toPathWorker(state, dir), fileOrDirectory, - fileOrDirectoryPath: toPath(state, fileOrDirectory), + fileOrDirectoryPath: toPathWorker(state, fileOrDirectory), configFileName: resolved, currentDirectory: state.compilerHost.getCurrentDirectory(), options: parsed.options, program: state.builderPrograms.get(resolvedPath) || getCachedParsedConfigFile(state, resolvedPath)?.fileNames, useCaseSensitiveFileNames: state.parseConfigFileHost.useCaseSensitiveFileNames, writeLog: s => state.writeLog(s), - toPath: fileName => toPath(state, fileName) + toPath: fileName => toPathWorker(state, fileName) })) return; invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.Partial); @@ -2384,7 +2412,7 @@ function watchInputFiles(state: SolutionBuilderState toPath(state, fileName)), + arrayToMap(parsed.fileNames, fileName => toPathWorker(state, fileName)), { createNewValue: (_path, input) => watchFile( state, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c97bbaffa2834..b9a986eef74ce 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,22 +1,23 @@ +import { ProgramBuildInfo } from "./builder"; +import { OptionsNameMap } from "./commandLineParser"; import { - BaseNodeFactory, - CreateSourceFileOptions, - EmitHelperFactory, GetCanonicalFileName, - MapLike, + MultiMap, + Pattern, +} from "./core"; +import { MapLike } from "./corePublic"; +import { BaseNodeFactory } from "./factory/baseNodeFactory"; +import { NodeFactoryFlags } from "./factory/nodeFactory"; +import { ModeAwareCache, ModeAwareCacheKey, ModuleResolutionCache, - MultiMap, - NodeFactoryFlags, - OptionsNameMap, PackageJsonInfo, PackageJsonInfoCache, - Pattern, - ProgramBuildInfo, - SymlinkCache, - ThisContainer, -} from "./_namespaces/ts"; +} from "./moduleNameResolver"; +import { CreateSourceFileOptions } from "./parser"; +import { SymlinkCache } from "./symlinkCache"; +import { ThisContainer } from "./utilities"; // branded string type used to store absolute, normalized and canonicalized paths // arbitrary file name can be converted to Path via toPath function @@ -5712,6 +5713,117 @@ export interface EmitResolver { isImportRequiredByAugmentation(decl: ImportDeclaration): boolean; } + + +/** @internal */ +export const enum TypeFacts { + None = 0, + TypeofEQString = 1 << 0, // typeof x === "string" + TypeofEQNumber = 1 << 1, // typeof x === "number" + TypeofEQBigInt = 1 << 2, // typeof x === "bigint" + TypeofEQBoolean = 1 << 3, // typeof x === "boolean" + TypeofEQSymbol = 1 << 4, // typeof x === "symbol" + TypeofEQObject = 1 << 5, // typeof x === "object" + TypeofEQFunction = 1 << 6, // typeof x === "function" + TypeofEQHostObject = 1 << 7, // typeof x === "xxx" + TypeofNEString = 1 << 8, // typeof x !== "string" + TypeofNENumber = 1 << 9, // typeof x !== "number" + TypeofNEBigInt = 1 << 10, // typeof x !== "bigint" + TypeofNEBoolean = 1 << 11, // typeof x !== "boolean" + TypeofNESymbol = 1 << 12, // typeof x !== "symbol" + TypeofNEObject = 1 << 13, // typeof x !== "object" + TypeofNEFunction = 1 << 14, // typeof x !== "function" + TypeofNEHostObject = 1 << 15, // typeof x !== "xxx" + EQUndefined = 1 << 16, // x === undefined + EQNull = 1 << 17, // x === null + EQUndefinedOrNull = 1 << 18, // x === undefined / x === null + NEUndefined = 1 << 19, // x !== undefined + NENull = 1 << 20, // x !== null + NEUndefinedOrNull = 1 << 21, // x != undefined / x != null + Truthy = 1 << 22, // x + Falsy = 1 << 23, // !x + IsUndefined = 1 << 24, // Contains undefined or intersection with undefined + IsNull = 1 << 25, // Contains null or intersection with null + IsUndefinedOrNull = IsUndefined | IsNull, + All = (1 << 27) - 1, + // The following members encode facts about particular kinds of types for use in the getTypeFacts function. + // The presence of a particular fact means that the given test is true for some (and possibly all) values + // of that kind of type. + BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, + BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, + StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy, + StringFacts = BaseStringFacts | Truthy, + EmptyStringStrictFacts = BaseStringStrictFacts | Falsy, + EmptyStringFacts = BaseStringFacts, + NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy, + NonEmptyStringFacts = BaseStringFacts | Truthy, + BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, + BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, + NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy, + NumberFacts = BaseNumberFacts | Truthy, + ZeroNumberStrictFacts = BaseNumberStrictFacts | Falsy, + ZeroNumberFacts = BaseNumberFacts, + NonZeroNumberStrictFacts = BaseNumberStrictFacts | Truthy, + NonZeroNumberFacts = BaseNumberFacts | Truthy, + BaseBigIntStrictFacts = TypeofEQBigInt | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, + BaseBigIntFacts = BaseBigIntStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, + BigIntStrictFacts = BaseBigIntStrictFacts | Truthy | Falsy, + BigIntFacts = BaseBigIntFacts | Truthy, + ZeroBigIntStrictFacts = BaseBigIntStrictFacts | Falsy, + ZeroBigIntFacts = BaseBigIntFacts, + NonZeroBigIntStrictFacts = BaseBigIntStrictFacts | Truthy, + NonZeroBigIntFacts = BaseBigIntFacts | Truthy, + BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull, + BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, + BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy, + BooleanFacts = BaseBooleanFacts | Truthy, + FalseStrictFacts = BaseBooleanStrictFacts | Falsy, + FalseFacts = BaseBooleanFacts, + TrueStrictFacts = BaseBooleanStrictFacts | Truthy, + TrueFacts = BaseBooleanFacts | Truthy, + SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, + ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, + FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, + VoidFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, + UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy | IsUndefined, + NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy | IsNull, + EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull | IsUndefinedOrNull), + EmptyObjectFacts = All & ~IsUndefinedOrNull, + UnknownFacts = All & ~IsUndefinedOrNull, + AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined, + // Masks + OrFactsMask = TypeofEQFunction | TypeofNEObject, + AndFactsMask = All & ~OrFactsMask, +} + +/** @internal */ +export const enum CheckMode { + Normal = 0, // Normal type checking + Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable + Inferential = 1 << 1, // Inferential typing + SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions + SkipGenericFunctions = 1 << 3, // Skip single signature generic functions + IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help + IsForStringLiteralArgumentCompletions = 1 << 5, // Do not infer from the argument currently being typed + RestBindingElement = 1 << 6, // Checking a type that is going to be used to determine the type of a rest binding element + // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`, + // we need to preserve generic types instead of substituting them for constraints +} + +/** @internal */ +export const enum SignatureCheckMode { + None = 0, + BivariantCallback = 1 << 0, + StrictCallback = 1 << 1, + IgnoreReturnTypes = 1 << 2, + StrictArity = 1 << 3, + StrictTopSignature = 1 << 4, + Callback = BivariantCallback | StrictCallback, +} + export const enum SymbolFlags { None = 0, FunctionScopedVariable = 1 << 0, // Variable (var) or parameter @@ -9974,3 +10086,275 @@ export interface Queue { dequeue(): T; isEmpty(): boolean; } + +/** + * Describes the decorator context object passed to a native ECMAScript decorator for a class. + * + * @internal + */ +export interface ESDecorateClassContext { + /** + * The kind of the decorated element. + */ + kind: "class"; + + /** + * The name of the decorated element. + */ + name: Expression; +} + +/** + * Describes the decorator context object passed to a native ECMAScript decorator for a class element. + * + * @internal + */ +export interface ESDecorateClassElementContext { + /** + * The kind of the decorated element. + */ + kind: "method" | "getter" | "setter" | "accessor" | "field"; + name: ESDecorateName; + static: boolean; + private: boolean; + access: ESDecorateClassElementAccess; +} + +/** @internal */ +export interface ESDecorateClassElementAccess { + get?: boolean; + set?: boolean; +} + +/** @internal */ +export type ESDecorateName = + | { computed: true, name: Expression } + | { computed: false, name: Identifier | PrivateIdentifier } + ; + +/** @internal */ +export type ESDecorateContext = + | ESDecorateClassContext + | ESDecorateClassElementContext + ; + +/** @internal */ +export interface EmitHelperFactory { + getUnscopedHelperName(name: string): Identifier; + // TypeScript Helpers + createDecorateHelper(decoratorExpressions: readonly Expression[], target: Expression, memberName?: Expression, descriptor?: Expression): Expression; + createMetadataHelper(metadataKey: string, metadataValue: Expression): Expression; + createParamHelper(expression: Expression, parameterOffset: number): Expression; + // ES Decorators Helpers + createESDecorateHelper(ctor: Expression, descriptorIn: Expression, decorators: Expression, contextIn: ESDecorateContext, initializers: Expression, extraInitializers: Expression): Expression; + createRunInitializersHelper(thisArg: Expression, initializers: Expression, value?: Expression): Expression; + // ES2018 Helpers + createAssignHelper(attributesSegments: readonly Expression[]): Expression; + createAwaitHelper(expression: Expression): Expression; + createAsyncGeneratorHelper(generatorFunc: FunctionExpression, hasLexicalThis: boolean): Expression; + createAsyncDelegatorHelper(expression: Expression): Expression; + createAsyncValuesHelper(expression: Expression): Expression; + // ES2018 Destructuring Helpers + createRestHelper(value: Expression, elements: readonly BindingOrAssignmentElement[], computedTempVariables: readonly Expression[] | undefined, location: TextRange): Expression; + // ES2017 Helpers + createAwaiterHelper(hasLexicalThis: boolean, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression | undefined, body: Block): Expression; + // ES2015 Helpers + createExtendsHelper(name: Identifier): Expression; + createTemplateObjectHelper(cooked: ArrayLiteralExpression, raw: ArrayLiteralExpression): Expression; + createSpreadArrayHelper(to: Expression, from: Expression, packFrom: boolean): Expression; + createPropKeyHelper(expr: Expression): Expression; + createSetFunctionNameHelper(f: Expression, name: Expression, prefix?: string): Expression; + // ES2015 Destructuring Helpers + createValuesHelper(expression: Expression): Expression; + createReadHelper(iteratorRecord: Expression, count: number | undefined): Expression; + // ES2015 Generator Helpers + createGeneratorHelper(body: FunctionExpression): Expression; + // ES Module Helpers + createCreateBindingHelper(module: Expression, inputName: Expression, outputName: Expression | undefined): Expression; + createImportStarHelper(expression: Expression): Expression; + createImportStarCallbackHelper(): Expression; + createImportDefaultHelper(expression: Expression): Expression; + createExportStarHelper(moduleExpression: Expression, exportsExpression?: Expression): Expression; + // Class Fields Helpers + createClassPrivateFieldGetHelper(receiver: Expression, state: Identifier, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression; + createClassPrivateFieldSetHelper(receiver: Expression, state: Identifier, value: Expression, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression; + createClassPrivateFieldInHelper(state: Identifier, receiver: Expression): Expression; +} + +/** @internal */ +export const enum PrivateIdentifierKind { + Field = "f", + Method = "m", + Accessor = "a" +} + +/** + * Safer version of `Function` which should not be called. + * Every function should be assignable to this, but this should not be assignable to every function. + * + * @internal + */ +export type AnyFunction = (...args: never[]) => void; + +/** @internal */ +export type AnyConstructor = new (...args: unknown[]) => unknown; + +/** @internal */ +export type Mutable = { -readonly [K in keyof T]: T[K] }; + +export type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex"; + +/** @internal */ +export interface NodeBuffer extends Uint8Array { + constructor: any; + write(str: string, encoding?: BufferEncoding): number; + write(str: string, offset: number, encoding?: BufferEncoding): number; + write(str: string, offset: number, length: number, encoding?: BufferEncoding): number; + toString(encoding?: string, start?: number, end?: number): string; + toJSON(): { type: "Buffer"; data: number[] }; + equals(otherBuffer: Uint8Array): boolean; + compare( + otherBuffer: Uint8Array, + targetStart?: number, + targetEnd?: number, + sourceStart?: number, + sourceEnd?: number + ): number; + copy(targetBuffer: Uint8Array, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; + slice(begin?: number, end?: number): Buffer; + subarray(begin?: number, end?: number): Buffer; + writeUIntLE(value: number, offset: number, byteLength: number): number; + writeUIntBE(value: number, offset: number, byteLength: number): number; + writeIntLE(value: number, offset: number, byteLength: number): number; + writeIntBE(value: number, offset: number, byteLength: number): number; + readUIntLE(offset: number, byteLength: number): number; + readUIntBE(offset: number, byteLength: number): number; + readIntLE(offset: number, byteLength: number): number; + readIntBE(offset: number, byteLength: number): number; + readUInt8(offset: number): number; + readUInt16LE(offset: number): number; + readUInt16BE(offset: number): number; + readUInt32LE(offset: number): number; + readUInt32BE(offset: number): number; + readInt8(offset: number): number; + readInt16LE(offset: number): number; + readInt16BE(offset: number): number; + readInt32LE(offset: number): number; + readInt32BE(offset: number): number; + readFloatLE(offset: number): number; + readFloatBE(offset: number): number; + readDoubleLE(offset: number): number; + readDoubleBE(offset: number): number; + reverse(): this; + swap16(): Buffer; + swap32(): Buffer; + swap64(): Buffer; + writeUInt8(value: number, offset: number): number; + writeUInt16LE(value: number, offset: number): number; + writeUInt16BE(value: number, offset: number): number; + writeUInt32LE(value: number, offset: number): number; + writeUInt32BE(value: number, offset: number): number; + writeInt8(value: number, offset: number): number; + writeInt16LE(value: number, offset: number): number; + writeInt16BE(value: number, offset: number): number; + writeInt32LE(value: number, offset: number): number; + writeInt32BE(value: number, offset: number): number; + writeFloatLE(value: number, offset: number): number; + writeFloatBE(value: number, offset: number): number; + writeDoubleLE(value: number, offset: number): number; + writeDoubleBE(value: number, offset: number): number; + readBigUInt64BE?(offset?: number): bigint; + readBigUInt64LE?(offset?: number): bigint; + readBigInt64BE?(offset?: number): bigint; + readBigInt64LE?(offset?: number): bigint; + writeBigInt64BE?(value: bigint, offset?: number): number; + writeBigInt64LE?(value: bigint, offset?: number): number; + writeBigUInt64BE?(value: bigint, offset?: number): number; + writeBigUInt64LE?(value: bigint, offset?: number): number; + fill(value: string | Uint8Array | number, offset?: number, end?: number, encoding?: BufferEncoding): this; + indexOf(value: string | number | Uint8Array, byteOffset?: number, encoding?: BufferEncoding): number; + lastIndexOf(value: string | number | Uint8Array, byteOffset?: number, encoding?: BufferEncoding): number; + entries(): IterableIterator<[number, number]>; + includes(value: string | number | Buffer, byteOffset?: number, encoding?: BufferEncoding): boolean; + keys(): IterableIterator; + values(): IterableIterator; +} + +/** @internal */ +export interface Buffer extends NodeBuffer { } + +export interface FileWatcher { + close(): void; +} + +export enum FileWatcherEventKind { + Created, + Changed, + Deleted +} + +export type FileWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, modifiedTime?: Date) => void; +export type DirectoryWatcherCallback = (fileName: string) => void; + +export interface System { + args: string[]; + newLine: string; + useCaseSensitiveFileNames: boolean; + write(s: string): void; + writeOutputIsTTY?(): boolean; + getWidthOfTerminal?(): number; + readFile(path: string, encoding?: string): string | undefined; + getFileSize?(path: string): number; + writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; + + /** + * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that + * use native OS file watching + */ + watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher; + watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher; + resolvePath(path: string): string; + fileExists(path: string): boolean; + directoryExists(path: string): boolean; + createDirectory(path: string): void; + getExecutingFilePath(): string; + getCurrentDirectory(): string; + getDirectories(path: string): string[]; + readDirectory(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[]; + getModifiedTime?(path: string): Date | undefined; + setModifiedTime?(path: string, time: Date): void; + deleteFile?(path: string): void; + /** + * A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm) + */ + createHash?(data: string): string; + /** This must be cryptographically secure. Only implement this method using `crypto.createHash("sha256")`. */ + createSHA256Hash?(data: string): string; + getMemoryUsage?(): number; + exit(exitCode?: number): void; + /** @internal */ enableCPUProfiler?(path: string, continuation: () => void): boolean; + /** @internal */ disableCPUProfiler?(continuation: () => void): boolean; + /** @internal */ cpuProfilingEnabled?(): boolean; + realpath?(path: string): string; + /** @internal */ getEnvironmentVariable(name: string): string; + /** @internal */ tryEnableSourceMapsForHost?(): void; + /** @internal */ debugMode?: boolean; + setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; + clearTimeout?(timeoutId: any): void; + clearScreen?(): void; + /** @internal */ setBlocking?(): void; + base64decode?(input: string): string; + base64encode?(input: string): string; + /** @internal */ bufferFrom?(input: string, encoding?: string): Buffer; + /** @internal */ require?(baseDir: string, moduleName: string): ModuleImportResult; + + // For testing + /** @internal */ now?(): Date; + /** @internal */ storeFilesChangingSignatureDuringEmit?: boolean; +} + +/** @internal */ +export interface ResolutionNameAndModeGetter { + getName(entry: Entry): string; + getMode(entry: Entry, file: SourceFile): ResolutionMode; +} diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index fde771ab80db5..16ac1cf838190 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1,10 +1,174 @@ +import { + addRange, + arrayFrom, + assertType, + binarySearch, + compareStringsCaseSensitive, + compareValues, + concatenate, + contains, + createGetCanonicalFileName, + emptyArray, + equalOwnProperties, + equateValues, + every, + filter, + find, + findBestPatternMatch, + findLast, + firstOrUndefined, + flatMap, + flatMapToMutable, + forEach, + GetCanonicalFileName, + getOwnKeys, + hasProperty, + identity, + insertSorted, + isArray, + isLineBreak, + isString, + isWhiteSpaceLike, + isWhiteSpaceSingleLine, + last, + lastOrUndefined, + length, + mapDefined, + noop, + Pattern, + singleElementArray, + singleOrUndefined, + some, + startsWith, + stringContains, + trimString, + trimStringStart, + tryCast, + tryRemovePrefix, +} from "./core"; +import { + Comparison, + EqualityComparer, + MapLike, + ReadonlyCollection, + SortedArray, + version, +} from "./corePublic"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { + isArrayLiteralExpression, + isArrowFunction, + isBigIntLiteral, + isBinaryExpression, + isBindingElement, + isCallExpression, + isClassDeclaration, + isClassExpression, + isClassStaticBlockDeclaration, + isCommaListExpression, + isComputedPropertyName, + isConstructorDeclaration, + isDecorator, + isElementAccessExpression, + isEnumDeclaration, + isEnumMember, + isExportAssignment, + isExportDeclaration, + isExpressionStatement, + isExpressionWithTypeArguments, + isExternalModuleReference, + isForStatement, + isFunctionDeclaration, + isFunctionExpression, + isGetAccessorDeclaration, + isHeritageClause, + isIdentifier, + isImportTypeNode, + isInterfaceDeclaration, + isJSDoc, + isJSDocAugmentsTag, + isJSDocFunctionType, + isJSDocMemberName, + isJSDocNameReference, + isJSDocOverloadTag, + isJSDocParameterTag, + isJSDocSatisfiesTag, + isJSDocSignature, + isJSDocTemplateTag, + isJSDocTypeExpression, + isJSDocTypeLiteral, + isJSDocTypeTag, + isJsxText, + isLiteralTypeNode, + isMetaProperty, + isMethodDeclaration, + isModuleDeclaration, + isNamespaceExport, + isNamespaceExportDeclaration, + isNamespaceImport, + isNonNullExpression, + isNoSubstitutionTemplateLiteral, + isNumericLiteral, + isObjectLiteralExpression, + isOmittedExpression, + isParameter, + isParenthesizedExpression, + isParenthesizedTypeNode, + isPrefixUnaryExpression, + isPrivateIdentifier, + isPropertyAccessExpression, + isPropertyAssignment, + isPropertyDeclaration, + isPropertySignature, + isQualifiedName, + isSetAccessorDeclaration, + isShorthandPropertyAssignment, + isSourceFile, + isStringLiteral, + isTypeAliasDeclaration, + isTypeLiteralNode, + isTypeParameterDeclaration, + isTypeReferenceNode, + isVariableDeclaration, + isVariableStatement, + isVoidExpression, +} from "./factory/nodeTests"; +import { + canHaveDecorators, + canHaveModifiers, +} from "./factory/utilitiesPublic"; +import { + forEachAncestorDirectory, + getBaseFileName, + getDirectoryPath, + getRootLength, + isAnyDirectorySeparator, + normalizePath, +} from "./path"; +import { + computeLineAndCharacterOfPosition, + computeLineOfPosition, + computeLineStarts, + createScanner, + getLeadingCommentRanges, + getLineAndCharacterOfPosition, + getLinesBetweenPositions, + getLineStarts, + getTrailingCommentRanges, + isIdentifierStart, + skipTrivia, + stringToToken, + tokenToString, +} from "./scanner"; +import { + parsePseudoBigInt, + positionIsSynthesized, +} from "./scannerUtilities"; import { __String, AccessExpression, AccessorDeclaration, - addRange, - affectsDeclarationPathOptionDeclarations, - affectsEmitOptionDeclarations, AliasDeclarationNode, AllAccessorDeclarations, AmbientModuleDeclaration, @@ -13,20 +177,16 @@ import { AnyImportOrReExport, AnyImportSyntax, AnyValidImportOrReExport, - append, - arrayFrom, ArrayLiteralExpression, ArrayTypeNode, ArrowFunction, AsExpression, AssertionExpression, - assertType, AssignmentDeclarationKind, AssignmentExpression, AssignmentOperatorToken, BarBarEqualsToken, BinaryExpression, - binarySearch, BindableObjectDefinePropertyCall, BindableStaticAccessExpression, BindableStaticElementAccessExpression, @@ -40,13 +200,10 @@ import { CallExpression, CallLikeExpression, CallSignatureDeclaration, - canHaveDecorators, - canHaveModifiers, CaseBlock, CaseClause, CaseOrDefaultClause, CatchClause, - changeAnyExtension, CharacterCodes, CheckFlags, ClassDeclaration, @@ -54,34 +211,17 @@ import { ClassExpression, ClassLikeDeclaration, ClassStaticBlockDeclaration, - combinePaths, CommaListExpression, CommandLineOption, CommentDirective, CommentDirectivesMap, CommentDirectiveType, CommentRange, - compareStringsCaseSensitive, - compareValues, - Comparison, CompilerOptions, ComputedPropertyName, - computeLineAndCharacterOfPosition, - computeLineOfPosition, - computeLineStarts, - concatenate, ConditionalExpression, ConstructorDeclaration, ConstructSignatureDeclaration, - contains, - containsPath, - createGetCanonicalFileName, - createModeAwareCache, - createMultiMap, - createScanner, - createTextSpan, - createTextSpanFromBounds, - Debug, Declaration, DeclarationName, DeclarationWithTypeParameterChildren, @@ -95,32 +235,19 @@ import { DiagnosticMessage, DiagnosticMessageChain, DiagnosticRelatedInformation, - Diagnostics, DiagnosticWithDetachedLocation, DiagnosticWithLocation, - directorySeparator, DoStatement, DynamicNamedBinaryExpression, DynamicNamedDeclaration, ElementAccessExpression, EmitFlags, - EmitHost, - EmitResolver, EmitTextWriter, - emptyArray, - endsWith, - ensurePathIsNonModuleName, - ensureTrailingDirectorySeparator, EntityName, EntityNameExpression, EntityNameOrEntityNameExpression, EnumDeclaration, - EqualityComparer, - equalOwnProperties, EqualsToken, - equateValues, - escapeLeadingUnderscores, - every, ExportAssignment, ExportDeclaration, ExportSpecifier, @@ -129,26 +256,7 @@ import { ExpressionWithTypeArguments, Extension, ExternalModuleReference, - factory, - FileExtensionInfo, - fileExtensionIs, - fileExtensionIsOneOf, FileWatcher, - filter, - find, - findAncestor, - findBestPatternMatch, - findIndex, - findLast, - firstDefined, - firstOrUndefined, - flatMap, - flatMapToMutable, - flatten, - forEach, - forEachAncestorDirectory, - forEachChild, - forEachChildRecursively, ForInOrOfStatement, ForInStatement, ForOfStatement, @@ -158,64 +266,19 @@ import { FunctionExpression, FunctionLikeDeclaration, GetAccessorDeclaration, - getBaseFileName, - GetCanonicalFileName, - getCombinedModifierFlags, - getCombinedNodeFlags, - getDirectoryPath, - getJSDocAugmentsTag, - getJSDocDeprecatedTagNoCache, - getJSDocImplementsTags, - getJSDocOverrideTagNoCache, - getJSDocParameterTags, - getJSDocParameterTagsNoCache, - getJSDocPrivateTagNoCache, - getJSDocProtectedTagNoCache, - getJSDocPublicTagNoCache, - getJSDocReadonlyTagNoCache, - getJSDocReturnType, - getJSDocSatisfiesTag, - getJSDocTags, - getJSDocType, - getJSDocTypeParameterTags, - getJSDocTypeParameterTagsNoCache, - getJSDocTypeTag, - getLeadingCommentRanges, - getLineAndCharacterOfPosition, - getLinesBetweenPositions, - getLineStarts, - getNameOfDeclaration, - getNormalizedAbsolutePath, - getNormalizedPathComponents, - getOwnKeys, - getParseTreeNode, - getPathComponents, - getPathFromPathComponents, - getRelativePathToDirectoryOrUrl, - getRootLength, - getSnippetElement, - getStringComparer, - getSymbolId, - getTrailingCommentRanges, HasExpressionInitializer, - hasExtension, HasFlowNode, + HasIllegalDecorators, + HasIllegalTypeParameters, HasInitializer, - hasInitializer, HasJSDoc, - hasJSDocNodes, HasModifiers, - hasProperty, HasType, HasTypeArguments, HeritageClause, Identifier, - identifierToKeywordKind, IdentifierTypePredicate, - identity, - idText, IfStatement, - ignoredPaths, ImportCall, ImportClause, ImportDeclaration, @@ -224,125 +287,10 @@ import { ImportSpecifier, ImportTypeNode, IndexInfo, - indexOfAnyCharCode, IndexSignatureDeclaration, InitializedVariableDeclaration, - insertSorted, InterfaceDeclaration, InternalEmitFlags, - isAccessor, - isAnyDirectorySeparator, - isArray, - isArrayLiteralExpression, - isArrowFunction, - isAutoAccessorPropertyDeclaration, - isBigIntLiteral, - isBinaryExpression, - isBindingElement, - isBindingPattern, - isCallExpression, - isClassDeclaration, - isClassElement, - isClassExpression, - isClassLike, - isClassStaticBlockDeclaration, - isCommaListExpression, - isComputedPropertyName, - isConstructorDeclaration, - isDeclaration, - isDecorator, - isElementAccessExpression, - isEnumDeclaration, - isEnumMember, - isExportAssignment, - isExportDeclaration, - isExpressionStatement, - isExpressionWithTypeArguments, - isExternalModule, - isExternalModuleReference, - isFileProbablyExternalModule, - isForStatement, - isFunctionDeclaration, - isFunctionExpression, - isFunctionLike, - isFunctionLikeDeclaration, - isFunctionLikeOrClassStaticBlockDeclaration, - isGetAccessorDeclaration, - isHeritageClause, - isIdentifier, - isIdentifierStart, - isIdentifierText, - isImportTypeNode, - isInterfaceDeclaration, - isJSDoc, - isJSDocAugmentsTag, - isJSDocFunctionType, - isJSDocLinkLike, - isJSDocMemberName, - isJSDocNameReference, - isJSDocNode, - isJSDocOverloadTag, - isJSDocParameterTag, - isJSDocPropertyLikeTag, - isJSDocSatisfiesTag, - isJSDocSignature, - isJSDocTag, - isJSDocTemplateTag, - isJSDocTypeExpression, - isJSDocTypeLiteral, - isJSDocTypeTag, - isJsxChild, - isJsxFragment, - isJsxOpeningLikeElement, - isJsxText, - isLeftHandSideExpression, - isLineBreak, - isLiteralTypeNode, - isMemberName, - isMetaProperty, - isMethodDeclaration, - isMethodOrAccessor, - isModifierLike, - isModuleDeclaration, - isNamedDeclaration, - isNamespaceExport, - isNamespaceExportDeclaration, - isNamespaceImport, - isNonNullExpression, - isNoSubstitutionTemplateLiteral, - isNumericLiteral, - isObjectLiteralExpression, - isOmittedExpression, - isParameter, - isParameterPropertyDeclaration, - isParenthesizedExpression, - isParenthesizedTypeNode, - isPrefixUnaryExpression, - isPrivateIdentifier, - isPropertyAccessExpression, - isPropertyAssignment, - isPropertyDeclaration, - isPropertyName, - isPropertySignature, - isQualifiedName, - isRootedDiskPath, - isSetAccessorDeclaration, - isShorthandPropertyAssignment, - isSourceFile, - isString, - isStringLiteral, - isStringLiteralLike, - isTypeAliasDeclaration, - isTypeElement, - isTypeLiteralNode, - isTypeNode, - isTypeParameterDeclaration, - isTypeReferenceNode, - isVariableDeclaration, - isVariableStatement, - isVoidExpression, - isWhiteSpaceLike, - isWhiteSpaceSingleLine, JSDoc, JSDocCallbackTag, JSDocEnumTag, @@ -354,6 +302,7 @@ import { JSDocSignature, JSDocTag, JSDocTemplateTag, + JSDocTypeAssertion, JSDocTypedefTag, JsonSourceFile, JsxAttributeName, @@ -369,22 +318,15 @@ import { KeywordSyntaxKind, LabeledStatement, LanguageVariant, - last, - lastOrUndefined, LateVisibilityPaintedStatement, - length, LiteralImportTypeNode, LiteralLikeElementAccessExpression, LiteralLikeNode, LogicalOperator, LogicalOrCoalescingAssignmentOperator, - map, - mapDefined, - MapLike, MemberName, MethodDeclaration, MethodSignature, - ModeAwareCache, ModifierFlags, ModifierLike, ModuleBlock, @@ -392,8 +334,7 @@ import { ModuleDetectionKind, ModuleKind, ModuleResolutionKind, - moduleResolutionOptionDeclarations, - MultiMap, + Mutable, NamedDeclaration, NamedExports, NamedImports, @@ -405,10 +346,7 @@ import { Node, NodeArray, NodeFlags, - nodeModulesPathPart, NonNullExpression, - noop, - normalizePath, NoSubstitutionTemplateLiteral, NumericLiteral, ObjectFlags, @@ -417,18 +355,13 @@ import { ObjectLiteralExpression, ObjectLiteralExpressionBase, ObjectTypeDeclaration, - optionsAffectingProgramStructure, - or, + OuterExpression, OuterExpressionKinds, PackageId, ParameterDeclaration, ParenthesizedExpression, ParenthesizedTypeNode, - parseConfigFileTextToJson, PartiallyEmittedExpression, - Path, - pathIsRelative, - Pattern, PostfixUnaryExpression, PrefixUnaryExpression, PrinterOptions, @@ -448,13 +381,10 @@ import { PunctuationSyntaxKind, QualifiedName, QuestionQuestionEqualsToken, - ReadonlyCollection, ReadonlyTextRange, - removeTrailingDirectorySeparator, RequireOrImportCall, RequireVariableStatement, ResolutionMode, - ResolutionNameAndModeGetter, ResolvedModuleFull, ResolvedModuleWithFailedLookupLocations, ResolvedTypeReferenceDirective, @@ -463,38 +393,24 @@ import { SatisfiesExpression, ScriptKind, ScriptTarget, - semanticDiagnosticsOptionDeclarations, SetAccessorDeclaration, ShorthandPropertyAssignment, - shouldAllowImportingTsExtension, Signature, SignatureDeclaration, SignatureFlags, - singleElementArray, - singleOrUndefined, - skipOuterExpressions, - skipTrivia, - SnippetKind, - some, - sort, - SortedArray, SourceFile, SourceFileLike, SourceFileMayBeEmittedHost, - SourceMapSource, - startsWith, - startsWithUseStrict, Statement, - stringContains, StringLiteral, StringLiteralLike, - stringToToken, SuperCall, SuperExpression, SuperProperty, SwitchStatement, Symbol, SymbolFlags, + SymbolId, SymbolTable, SyntaxKind, SyntaxList, @@ -508,16 +424,8 @@ import { ThisTypePredicate, Token, TokenFlags, - tokenToString, - toPath, - tracing, - TransformFlags, TransientSymbol, - trimString, - trimStringStart, TriviaSyntaxKind, - tryCast, - tryRemovePrefix, TryStatement, TsConfigSourceFile, TupleTypeNode, @@ -535,23 +443,102 @@ import { TypePredicate, TypePredicateKind, TypeReferenceNode, + UnionOrIntersectionTypeNode, + ValidImportTypeNode, + VariableDeclaration, + VariableDeclarationInitializedTo, + VariableDeclarationList, + VariableLikeDeclaration, + VariableStatement, + WhileStatement, + WithStatement, + WrappedExpression, + WriteFileCallback, + WriteFileCallbackData, + YieldExpression, +} from "./types"; +import { + createTextSpan, + createTextSpanFromBounds, + escapeLeadingUnderscores, + findAncestor, + getCombinedModifierFlags, + getCombinedNodeFlags, + getJSDocAugmentsTag, + getJSDocDeprecatedTagNoCache, + getJSDocImplementsTags, + getJSDocOverrideTagNoCache, + getJSDocParameterTags, + getJSDocParameterTagsNoCache, + getJSDocPrivateTagNoCache, + getJSDocProtectedTagNoCache, + getJSDocPublicTagNoCache, + getJSDocReadonlyTagNoCache, + getJSDocReturnType, + getJSDocSatisfiesTag, + getJSDocTags, + getJSDocType, + getJSDocTypeParameterTags, + getJSDocTypeParameterTagsNoCache, + getJSDocTypeTag, + getNameOfDeclaration, + getParseTreeNode, + hasInitializer, + hasJSDocNodes, + identifierToKeywordKind, + idText, + isAccessor, + isAutoAccessorPropertyDeclaration, + isBindingPattern, + isClassElement, + isClassLike, + isDeclaration, + isFunctionLike, + isFunctionLikeDeclaration, + isFunctionLikeOrClassStaticBlockDeclaration, + isJSDocLinkLike, + isJSDocNode, + isJSDocPropertyLikeTag, + isJSDocTag, + isJsxChild, + isLeftHandSideExpression, + isMemberName, + isMethodOrAccessor, + isModifierLike, + isNamedDeclaration, + isParameterPropertyDeclaration, + isPropertyName, + isStringLiteralLike, + isTypeElement, + isTypeNode, unescapeLeadingUnderscores, - UnionOrIntersectionTypeNode, - UserPreferences, - ValidImportTypeNode, - VariableDeclaration, - VariableDeclarationInitializedTo, - VariableDeclarationList, - VariableLikeDeclaration, - VariableStatement, - version, - WhileStatement, - WithStatement, - WrappedExpression, - WriteFileCallback, - WriteFileCallbackData, - YieldExpression, -} from "./_namespaces/ts"; +} from "./utilitiesPublic"; + +function isCommonJSContainingModuleKind(kind: ModuleKind) { + return kind === ModuleKind.CommonJS || kind === ModuleKind.Node16 || kind === ModuleKind.NodeNext; +} + +let nextSymbolId = 1; +let nextNodeId = 1; + +/** @internal */ +export function getNodeId(node: Node): number { + if (!node.id) { + node.id = nextNodeId; + nextNodeId++; + } + return node.id; +} + +/** @internal */ +export function getSymbolId(symbol: Symbol): SymbolId { + if (!symbol.id) { + symbol.id = nextSymbolId; + nextSymbolId++; + } + + return symbol.id; +} /** @internal */ export const resolvingEmptyArray: never[] = []; @@ -578,6 +565,11 @@ export function getDeclarationOfKind(symbol: Symbol, kind return undefined; } +// See also `isExternalOrCommonJsModule` +export function isExternalModule(file: SourceFile): boolean { + return file.externalModuleIndicator !== undefined; +} + /** @internal */ export function getDeclarationsOfKind(symbol: Symbol, kind: T["kind"]): T[] { return filter(symbol.declarations || emptyArray, d => d.kind === kind) as T[]; @@ -640,22 +632,6 @@ function createSingleLineStringWriter(): EmitTextWriter { }; } -/** @internal */ -export function changesAffectModuleResolution(oldOptions: CompilerOptions, newOptions: CompilerOptions): boolean { - return oldOptions.configFilePath !== newOptions.configFilePath || - optionsHaveModuleResolutionChanges(oldOptions, newOptions); -} - -/** @internal */ -export function optionsHaveModuleResolutionChanges(oldOptions: CompilerOptions, newOptions: CompilerOptions) { - return optionsHaveChanges(oldOptions, newOptions, moduleResolutionOptionDeclarations); -} - -/** @internal */ -export function changesAffectingProgramStructure(oldOptions: CompilerOptions, newOptions: CompilerOptions) { - return optionsHaveChanges(oldOptions, newOptions, optionsAffectingProgramStructure); -} - /** @internal */ export function optionsHaveChanges(oldOptions: CompilerOptions, newOptions: CompilerOptions, optionDeclarations: readonly CommandLineOption[]) { return oldOptions !== newOptions && optionDeclarations.some(o => @@ -740,24 +716,6 @@ export function getResolvedModule(sourceFile: SourceFile | undefined, moduleName return sourceFile?.resolvedModules?.get(moduleNameText, mode)?.resolvedModule; } -/** @internal */ -export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleWithFailedLookupLocations, mode: ResolutionMode): void { - if (!sourceFile.resolvedModules) { - sourceFile.resolvedModules = createModeAwareCache(); - } - - sourceFile.resolvedModules.set(moduleNameText, mode, resolvedModule); -} - -/** @internal */ -export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirectiveWithFailedLookupLocations, mode: ResolutionMode): void { - if (!sourceFile.resolvedTypeReferenceDirectiveNames) { - sourceFile.resolvedTypeReferenceDirectiveNames = createModeAwareCache(); - } - - sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, mode, resolvedTypeReferenceDirective); -} - /** @internal */ export function getResolvedTypeReferenceDirective(sourceFile: SourceFile | undefined, typeReferenceDirectiveName: string, mode: ResolutionMode): ResolvedTypeReferenceDirective | undefined { return sourceFile?.resolvedTypeReferenceDirectiveNames?.get(typeReferenceDirectiveName, mode)?.resolvedTypeReferenceDirective; @@ -808,61 +766,6 @@ export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirec oldResolution.resolvedTypeReferenceDirective.originalPath === newResolution.resolvedTypeReferenceDirective.originalPath; } -/** @internal */ -export function hasChangesInResolutions( - names: readonly K[], - newSourceFile: SourceFile, - newResolutions: readonly V[], - oldResolutions: ModeAwareCache | undefined, - comparer: (oldResolution: V, newResolution: V) => boolean, - nameAndModeGetter: ResolutionNameAndModeGetter, -): boolean { - Debug.assert(names.length === newResolutions.length); - - for (let i = 0; i < names.length; i++) { - const newResolution = newResolutions[i]; - const entry = names[i]; - const name = nameAndModeGetter.getName(entry); - const mode = nameAndModeGetter.getMode(entry, newSourceFile); - const oldResolution = oldResolutions && oldResolutions.get(name, mode); - const changed = - oldResolution - ? !newResolution || !comparer(oldResolution, newResolution) - : newResolution; - if (changed) { - return true; - } - } - return false; -} - -// Returns true if this node contains a parse error anywhere underneath it. -/** @internal */ -export function containsParseError(node: Node): boolean { - aggregateChildData(node); - return (node.flags & NodeFlags.ThisNodeOrAnySubNodesHasError) !== 0; -} - -function aggregateChildData(node: Node): void { - if (!(node.flags & NodeFlags.HasAggregatedChildData)) { - // A node is considered to contain a parse error if: - // a) the parser explicitly marked that it had an error - // b) any of it's children reported that it had an error. - const thisNodeOrAnySubNodesHasError = ((node.flags & NodeFlags.ThisNodeHasError) !== 0) || - forEachChild(node, containsParseError); - - // If so, mark ourselves accordingly. - if (thisNodeOrAnySubNodesHasError) { - (node as Mutable).flags |= NodeFlags.ThisNodeOrAnySubNodesHasError; - } - - // Also mark that we've propagated the child information to this node. This way we can - // always consult the bit directly on this node without needing to check its children - // again. - (node as Mutable).flags |= NodeFlags.HasAggregatedChildData; - } -} - /** @internal */ export function getSourceFileOfNode(node: Node): SourceFile; /** @internal */ @@ -875,11 +778,6 @@ export function getSourceFileOfNode(node: Node | undefined): SourceFile | undefi return node as SourceFile; } -/** @internal */ -export function getSourceFileOfModule(module: Symbol) { - return getSourceFileOfNode(module.valueDeclaration || getNonAugmentationDeclaration(module)); -} - /** @internal */ export function isPlainJsFile(file: SourceFile | undefined, checkJs: boolean | undefined): boolean { return !!file && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX) && !file.checkJsDirective && checkJs === undefined; @@ -1741,11 +1639,6 @@ export function isCatchClauseVariableDeclarationOrBindingElement(declaration: De return node.kind === SyntaxKind.VariableDeclaration && node.parent.kind === SyntaxKind.CatchClause; } -/** @internal */ -export function isAmbientModule(node: Node): node is AmbientModuleDeclaration { - return isModuleDeclaration(node) && (node.name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(node)); -} - /** @internal */ export function isModuleWithStringLiteralName(node: Node): node is ModuleDeclaration { return isModuleDeclaration(node) && node.name.kind === SyntaxKind.StringLiteral; @@ -1789,83 +1682,6 @@ export function isBlockScopedContainerTopLevel(node: Node): boolean { isFunctionLikeOrClassStaticBlockDeclaration(node); } -/** @internal */ -export function isGlobalScopeAugmentation(module: ModuleDeclaration): boolean { - return !!(module.flags & NodeFlags.GlobalAugmentation); -} - -/** @internal */ -export function isExternalModuleAugmentation(node: Node): node is AmbientModuleDeclaration { - return isAmbientModule(node) && isModuleAugmentationExternal(node); -} - -/** @internal */ -export function isModuleAugmentationExternal(node: AmbientModuleDeclaration) { - // external module augmentation is a ambient module declaration that is either: - // - defined in the top level scope and source file is an external module - // - defined inside ambient module declaration located in the top level scope and source file not an external module - switch (node.parent.kind) { - case SyntaxKind.SourceFile: - return isExternalModule(node.parent); - case SyntaxKind.ModuleBlock: - return isAmbientModule(node.parent.parent) && isSourceFile(node.parent.parent.parent) && !isExternalModule(node.parent.parent.parent); - } - return false; -} - -/** @internal */ -export function getNonAugmentationDeclaration(symbol: Symbol) { - return symbol.declarations?.find(d => !isExternalModuleAugmentation(d) && !(isModuleDeclaration(d) && isGlobalScopeAugmentation(d))); -} - -function isCommonJSContainingModuleKind(kind: ModuleKind) { - return kind === ModuleKind.CommonJS || kind === ModuleKind.Node16 || kind === ModuleKind.NodeNext; -} - -/** @internal */ -export function isEffectiveExternalModule(node: SourceFile, compilerOptions: CompilerOptions) { - return isExternalModule(node) || getIsolatedModules(compilerOptions) || (isCommonJSContainingModuleKind(getEmitModuleKind(compilerOptions)) && !!node.commonJsModuleIndicator); -} - -/** - * Returns whether the source file will be treated as if it were in strict mode at runtime. - * - * @internal - */ -export function isEffectiveStrictModeSourceFile(node: SourceFile, compilerOptions: CompilerOptions) { - // We can only verify strict mode for JS/TS files - switch (node.scriptKind) { - case ScriptKind.JS: - case ScriptKind.TS: - case ScriptKind.JSX: - case ScriptKind.TSX: - break; - default: - return false; - } - // Strict mode does not matter for declaration files. - if (node.isDeclarationFile) { - return false; - } - // If `alwaysStrict` is set, then treat the file as strict. - if (getStrictOptionValue(compilerOptions, "alwaysStrict")) { - return true; - } - // Starting with a "use strict" directive indicates the file is strict. - if (startsWithUseStrict(node.statements)) { - return true; - } - if (isExternalModule(node) || getIsolatedModules(compilerOptions)) { - // ECMAScript Modules are always strict. - if (getEmitModuleKind(compilerOptions) >= ModuleKind.ES2015) { - return true; - } - // Other modules are strict unless otherwise specified. - return !compilerOptions.noImplicitUseStrict; - } - return false; -} - /** @internal */ export function isAmbientPropertyDeclaration(node: PropertyDeclaration) { return !!(node.flags & NodeFlags.Ambient) || hasSyntacticModifier(node, ModifierFlags.Ambient); @@ -1903,7 +1719,7 @@ export function isBlockScope(node: Node, parentNode: Node | undefined): boolean /** @internal */ export function isDeclarationWithTypeParameters(node: Node): node is DeclarationWithTypeParameters { - Debug.type(node); + Debug.assertType(node); switch (node.kind) { case SyntaxKind.JSDocCallbackTag: case SyntaxKind.JSDocTypedefTag: @@ -1917,7 +1733,7 @@ export function isDeclarationWithTypeParameters(node: Node): node is Declaration /** @internal */ export function isDeclarationWithTypeParameterChildren(node: Node): node is DeclarationWithTypeParameterChildren { - Debug.type(node); + Debug.assertType(node); switch (node.kind) { case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: @@ -2495,75 +2311,6 @@ export function isChildOfNodeWithKind(node: Node, kind: SyntaxKind): boolean { return false; } -// Warning: This has the same semantics as the forEach family of functions, -// in that traversal terminates in the event that 'visitor' supplies a truthy value. -/** @internal */ -export function forEachReturnStatement(body: Block | Statement, visitor: (stmt: ReturnStatement) => T): T | undefined { - - return traverse(body); - - function traverse(node: Node): T | undefined { - switch (node.kind) { - case SyntaxKind.ReturnStatement: - return visitor(node as ReturnStatement); - case SyntaxKind.CaseBlock: - case SyntaxKind.Block: - case SyntaxKind.IfStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.WithStatement: - case SyntaxKind.SwitchStatement: - case SyntaxKind.CaseClause: - case SyntaxKind.DefaultClause: - case SyntaxKind.LabeledStatement: - case SyntaxKind.TryStatement: - case SyntaxKind.CatchClause: - return forEachChild(node, traverse); - } - } -} - -/** @internal */ -export function forEachYieldExpression(body: Block, visitor: (expr: YieldExpression) => void): void { - - return traverse(body); - - function traverse(node: Node): void { - switch (node.kind) { - case SyntaxKind.YieldExpression: - visitor(node as YieldExpression); - const operand = (node as YieldExpression).expression; - if (operand) { - traverse(operand); - } - return; - case SyntaxKind.EnumDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.TypeAliasDeclaration: - // These are not allowed inside a generator now, but eventually they may be allowed - // as local types. Regardless, skip them to avoid the work. - return; - default: - if (isFunctionLike(node)) { - if (node.name && node.name.kind === SyntaxKind.ComputedPropertyName) { - // Note that we will not include methods/accessors of a class because they would require - // first descending into the class. This is by design. - traverse(node.name.expression); - return; - } - } - else if (!isPartOfTypeNode(node)) { - // This is the general case, which should include mostly expressions and statements. - // Also includes NodeArrays. - forEachChild(node, traverse); - } - } - } -} /** * Gets the most likely element type for a TypeNode. This is not an exhaustive test @@ -6072,101 +5819,6 @@ export function hostGetCanonicalFileName(host: { useCaseSensitiveFileNames?(): b return createGetCanonicalFileName(hostUsesCaseSensitiveFileNames(host)); } -/** @internal */ -export interface ResolveModuleNameResolutionHost { - getCanonicalFileName(p: string): string; - getCommonSourceDirectory(): string; - getCurrentDirectory(): string; -} - -/** @internal */ -export function getResolvedExternalModuleName(host: ResolveModuleNameResolutionHost, file: SourceFile, referenceFile?: SourceFile): string { - return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName); -} - -function getCanonicalAbsolutePath(host: ResolveModuleNameResolutionHost, path: string) { - return host.getCanonicalFileName(getNormalizedAbsolutePath(path, host.getCurrentDirectory())); -} - -/** @internal */ -export function getExternalModuleNameFromDeclaration(host: ResolveModuleNameResolutionHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string | undefined { - const file = resolver.getExternalModuleFileFromDeclaration(declaration); - if (!file || file.isDeclarationFile) { - return undefined; - } - // If the declaration already uses a non-relative name, and is outside the common source directory, continue to use it - const specifier = getExternalModuleName(declaration); - if (specifier && isStringLiteralLike(specifier) && !pathIsRelative(specifier.text) && - getCanonicalAbsolutePath(host, file.path).indexOf(getCanonicalAbsolutePath(host, ensureTrailingDirectorySeparator(host.getCommonSourceDirectory()))) === -1) { - return undefined; - } - return getResolvedExternalModuleName(host, file); -} - -/** - * Resolves a local path to a path which is absolute to the base of the emit - * - * @internal - */ -export function getExternalModuleNameFromPath(host: ResolveModuleNameResolutionHost, fileName: string, referencePath?: string): string { - const getCanonicalFileName = (f: string) => host.getCanonicalFileName(f); - const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName); - const filePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory()); - const relativePath = getRelativePathToDirectoryOrUrl(dir, filePath, dir, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false); - const extensionless = removeFileExtension(relativePath); - return referencePath ? ensurePathIsNonModuleName(extensionless) : extensionless; -} - -/** @internal */ -export function getOwnEmitOutputFilePath(fileName: string, host: EmitHost, extension: string) { - const compilerOptions = host.getCompilerOptions(); - let emitOutputFilePathWithoutExtension: string; - if (compilerOptions.outDir) { - emitOutputFilePathWithoutExtension = removeFileExtension(getSourceFilePathInNewDir(fileName, host, compilerOptions.outDir)); - } - else { - emitOutputFilePathWithoutExtension = removeFileExtension(fileName); - } - - return emitOutputFilePathWithoutExtension + extension; -} - -/** @internal */ -export function getDeclarationEmitOutputFilePath(fileName: string, host: EmitHost) { - return getDeclarationEmitOutputFilePathWorker(fileName, host.getCompilerOptions(), host.getCurrentDirectory(), host.getCommonSourceDirectory(), f => host.getCanonicalFileName(f)); -} - -/** @internal */ -export function getDeclarationEmitOutputFilePathWorker(fileName: string, options: CompilerOptions, currentDirectory: string, commonSourceDirectory: string, getCanonicalFileName: GetCanonicalFileName): string { - const outputDir = options.declarationDir || options.outDir; // Prefer declaration folder if specified - - const path = outputDir - ? getSourceFilePathInNewDirWorker(fileName, outputDir, currentDirectory, commonSourceDirectory, getCanonicalFileName) - : fileName; - const declarationExtension = getDeclarationEmitExtensionForPath(path); - return removeFileExtension(path) + declarationExtension; -} - -/** @internal */ -export function getDeclarationEmitExtensionForPath(path: string) { - return fileExtensionIsOneOf(path, [Extension.Mjs, Extension.Mts]) ? Extension.Dmts : - fileExtensionIsOneOf(path, [Extension.Cjs, Extension.Cts]) ? Extension.Dcts : - fileExtensionIsOneOf(path, [Extension.Json]) ? `.d.json.ts` : // Drive-by redefinition of json declaration file output name so if it's ever enabled, it behaves well - Extension.Dts; -} - -/** - * This function is an inverse of `getDeclarationEmitExtensionForPath`. - * - * @internal - */ -export function getPossibleOriginalInputExtensionForExtension(path: string) { - return fileExtensionIsOneOf(path, [Extension.Dmts, Extension.Mjs, Extension.Mts]) ? [Extension.Mts, Extension.Mjs] : - fileExtensionIsOneOf(path, [Extension.Dcts, Extension.Cjs, Extension.Cts]) ? [Extension.Cts, Extension.Cjs]: - fileExtensionIsOneOf(path, [`.d.json.ts`]) ? [Extension.Json] : - [Extension.Tsx, Extension.Ts, Extension.Jsx, Extension.Js]; -} - /** @internal */ export function outFile(options: CompilerOptions) { return options.outFile || options.out; @@ -6191,39 +5843,6 @@ export interface EmitFileNames { buildInfoPath?: string | undefined; } -/** - * Gets the source files that are expected to have an emit output. - * - * Originally part of `forEachExpectedEmitFile`, this functionality was extracted to support - * transformations. - * - * @param host An EmitHost. - * @param targetSourceFile An optional target source file to emit. - * - * @internal - */ -export function getSourceFilesToEmit(host: EmitHost, targetSourceFile?: SourceFile, forceDtsEmit?: boolean): readonly SourceFile[] { - const options = host.getCompilerOptions(); - if (outFile(options)) { - const moduleKind = getEmitModuleKind(options); - const moduleEmitEnabled = options.emitDeclarationOnly || moduleKind === ModuleKind.AMD || moduleKind === ModuleKind.System; - // Can emit only sources that are not declaration file and are either non module code or module with --module or --target es6 specified - return filter( - host.getSourceFiles(), - sourceFile => - (moduleEmitEnabled || !isExternalModule(sourceFile)) && - sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit) - ); - } - else { - const sourceFiles = targetSourceFile === undefined ? host.getSourceFiles() : [targetSourceFile]; - return filter( - sourceFiles, - sourceFile => sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit) - ); - } -} - /** * Don't call this for `--outFile`, just for `--outDir` or plain emit. `--outFile` needs additional checks. * @@ -6240,19 +5859,6 @@ export function sourceFileMayBeEmitted(sourceFile: SourceFile, host: SourceFileM )); } -/** @internal */ -export function getSourceFilePathInNewDir(fileName: string, host: EmitHost, newDirPath: string): string { - return getSourceFilePathInNewDirWorker(fileName, newDirPath, host.getCurrentDirectory(), host.getCommonSourceDirectory(), f => host.getCanonicalFileName(f)); -} - -/** @internal */ -export function getSourceFilePathInNewDirWorker(fileName: string, newDirPath: string, currentDirectory: string, commonSourceDirectory: string, getCanonicalFileName: GetCanonicalFileName): string { - let sourceFilePath = getNormalizedAbsolutePath(fileName, currentDirectory); - const isSourceFileInCommonSourceDirectory = getCanonicalFileName(sourceFilePath).indexOf(getCanonicalFileName(commonSourceDirectory)) === 0; - sourceFilePath = isSourceFileInCommonSourceDirectory ? sourceFilePath.substring(commonSourceDirectory.length) : sourceFilePath; - return combinePaths(newDirPath, sourceFilePath); -} - /** @internal */ export function writeFile(host: { writeFile: WriteFileCallback; }, diagnostics: DiagnosticCollection, fileName: string, text: string, writeByteOrderMark: boolean, sourceFiles?: readonly SourceFile[], data?: WriteFileCallbackData) { host.writeFile(fileName, text, writeByteOrderMark, hostErrorMessage => { @@ -7093,14 +6699,6 @@ function isExportDefaultSymbol(symbol: Symbol): boolean { return symbol && length(symbol.declarations) > 0 && hasSyntacticModifier(symbol.declarations![0], ModifierFlags.Default); } -/** - * Return ".ts", ".d.ts", or ".tsx", if that is the extension. - * - * @internal - */ -export function tryExtractTSExtension(fileName: string): string | undefined { - return find(supportedTSExtensionsForExtractExtension, extension => fileExtensionIs(fileName, extension)); -} /** * Replace each instance of non-ascii characters by one, two, three, or four escape sequences * representing the UTF-8 encoding of the character, and return the expanded char code list. @@ -7257,20 +6855,6 @@ export function base64decode(host: { base64decode?(input: string): string } | un return getStringFromExpandedCharCodes(expandedCharCodes); } -/** @internal */ -export function readJsonOrUndefined(path: string, hostOrText: { readFile(fileName: string): string | undefined } | string): object | undefined { - const jsonText = isString(hostOrText) ? hostOrText : hostOrText.readFile(path); - if (!jsonText) return undefined; - // gracefully handle if readFile fails or returns not JSON - const result = parseConfigFileTextToJson(path, jsonText); - return !result.error ? result.config : undefined; -} - -/** @internal */ -export function readJson(path: string, host: { readFile(fileName: string): string | undefined }): object { - return readJsonOrUndefined(path, host) || {}; -} - /** @internal */ export function directoryProbablyExists(directoryName: string, host: { directoryExists?: (directoryName: string) => boolean }): boolean { // if host does not support 'directoryExists' assume that directory will exist @@ -7279,6 +6863,7 @@ export function directoryProbablyExists(directoryName: string, host: { directory const carriageReturnLineFeed = "\r\n"; const lineFeed = "\n"; + /** @internal */ export function getNewLineCharacter(options: CompilerOptions | PrinterOptions): string { switch (options.newLine) { @@ -7720,28 +7305,9 @@ export function isUMDExportSymbol(symbol: Symbol | undefined): boolean { return !!symbol && !!symbol.declarations && !!symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]); } -/** @internal */ -export function showModuleSpecifier({ moduleSpecifier }: ImportDeclaration): string { - return isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : getTextOfNode(moduleSpecifier); -} - -/** @internal */ -export function getLastChild(node: Node): Node | undefined { - let lastChild: Node | undefined; - forEachChild(node, - child => { - if (nodeIsPresent(child)) lastChild = child; - }, - children => { - // As an optimization, jump straight to the end of the list. - for (let i = children.length - 1; i >= 0; i--) { - if (nodeIsPresent(children[i])) { - lastChild = children[i]; - break; - } - } - }); - return lastChild; +/** @internal */ +export function showModuleSpecifier({ moduleSpecifier }: ImportDeclaration): string { + return isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : getTextOfNode(moduleSpecifier); } /** @@ -7908,122 +7474,6 @@ export function getLeftmostExpression(node: Expression, stopAtCallExpressions: b } } -/** @internal */ -export interface ObjectAllocator { - getNodeConstructor(): new (kind: SyntaxKind, pos: number, end: number) => Node; - getTokenConstructor(): new (kind: TKind, pos: number, end: number) => Token; - getIdentifierConstructor(): new (kind: SyntaxKind.Identifier, pos: number, end: number) => Identifier; - getPrivateIdentifierConstructor(): new (kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) => PrivateIdentifier; - getSourceFileConstructor(): new (kind: SyntaxKind.SourceFile, pos: number, end: number) => SourceFile; - getSymbolConstructor(): new (flags: SymbolFlags, name: __String) => Symbol; - getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type; - getSignatureConstructor(): new (checker: TypeChecker, flags: SignatureFlags) => Signature; - getSourceMapSourceConstructor(): new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource; -} - -function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { - this.flags = flags; - this.escapedName = name; - this.declarations = undefined; - this.valueDeclaration = undefined; - this.id = 0; - this.mergeId = 0; - this.parent = undefined; - this.members = undefined; - this.exports = undefined; - this.exportSymbol = undefined; - this.constEnumOnlyModule = undefined; - this.isReferenced = undefined; - this.isAssigned = undefined; - (this as any).links = undefined; // used by TransientSymbol -} - -function Type(this: Type, checker: TypeChecker, flags: TypeFlags) { - this.flags = flags; - if (Debug.isDebugging || tracing) { - this.checker = checker; - } -} - -function Signature(this: Signature, checker: TypeChecker, flags: SignatureFlags) { - this.flags = flags; - if (Debug.isDebugging) { - this.checker = checker; - } -} - -function Node(this: Mutable, kind: SyntaxKind, pos: number, end: number) { - this.pos = pos; - this.end = end; - this.kind = kind; - this.id = 0; - this.flags = NodeFlags.None; - this.modifierFlagsCache = ModifierFlags.None; - this.transformFlags = TransformFlags.None; - this.parent = undefined!; - this.original = undefined; - this.emitNode = undefined; -} - -function Token(this: Mutable, kind: SyntaxKind, pos: number, end: number) { - this.pos = pos; - this.end = end; - this.kind = kind; - this.id = 0; - this.flags = NodeFlags.None; - this.transformFlags = TransformFlags.None; - this.parent = undefined!; - this.emitNode = undefined; -} - -function Identifier(this: Mutable, kind: SyntaxKind, pos: number, end: number) { - this.pos = pos; - this.end = end; - this.kind = kind; - this.id = 0; - this.flags = NodeFlags.None; - this.transformFlags = TransformFlags.None; - this.parent = undefined!; - this.original = undefined; - this.emitNode = undefined; -} - -function SourceMapSource(this: SourceMapSource, fileName: string, text: string, skipTrivia?: (pos: number) => number) { - this.fileName = fileName; - this.text = text; - this.skipTrivia = skipTrivia || (pos => pos); -} - -/** @internal */ -export const objectAllocator: ObjectAllocator = { - getNodeConstructor: () => Node as any, - getTokenConstructor: () => Token as any, - getIdentifierConstructor: () => Identifier as any, - getPrivateIdentifierConstructor: () => Node as any, - getSourceFileConstructor: () => Node as any, - getSymbolConstructor: () => Symbol as any, - getTypeConstructor: () => Type as any, - getSignatureConstructor: () => Signature as any, - getSourceMapSourceConstructor: () => SourceMapSource as any, -}; - -const objectAllocatorPatchers: ((objectAllocator: ObjectAllocator) => void)[] = []; - -/** - * Used by `deprecatedCompat` to patch the object allocator to apply deprecations. - * @internal - */ -export function addObjectAllocatorPatcher(fn: (objectAllocator: ObjectAllocator) => void) { - objectAllocatorPatchers.push(fn); - fn(objectAllocator); -} - -/** @internal */ -export function setObjectAllocator(alloc: ObjectAllocator) { - Object.assign(objectAllocator, alloc); - forEach(objectAllocatorPatchers, fn => fn(objectAllocator)); -} - /** @internal */ export function formatStringFromArgs(text: string, args: ArrayLike, baseIndex = 0): string { return text.replace(/{(\d+)}/g, (_match, index: string) => "" + Debug.checkDefined(args[+index + baseIndex])); @@ -8303,62 +7753,6 @@ export function getLanguageVariant(scriptKind: ScriptKind) { return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSON ? LanguageVariant.JSX : LanguageVariant.Standard; } -/** - * This is a somewhat unavoidable full tree walk to locate a JSX tag - `import.meta` requires the same, - * but we avoid that walk (or parts of it) if at all possible using the `PossiblyContainsImportMeta` node flag. - * Unfortunately, there's no `NodeFlag` space to do the same for JSX. - */ -function walkTreeForJSXTags(node: Node): Node | undefined { - if (!(node.transformFlags & TransformFlags.ContainsJsx)) return undefined; - return isJsxOpeningLikeElement(node) || isJsxFragment(node) ? node : forEachChild(node, walkTreeForJSXTags); -} - -function isFileModuleFromUsingJSXTag(file: SourceFile): Node | undefined { - // Excludes declaration files - they still require an explicit `export {}` or the like - // for back compat purposes. (not that declaration files should contain JSX tags!) - return !file.isDeclarationFile ? walkTreeForJSXTags(file) : undefined; -} - -/** - * Note that this requires file.impliedNodeFormat be set already; meaning it must be set very early on - * in SourceFile construction. - */ -function isFileForcedToBeModuleByFormat(file: SourceFile): true | undefined { - // Excludes declaration files - they still require an explicit `export {}` or the like - // for back compat purposes. The only non-declaration files _not_ forced to be a module are `.js` files - // that aren't esm-mode (meaning not in a `type: module` scope). - return (file.impliedNodeFormat === ModuleKind.ESNext || (fileExtensionIsOneOf(file.fileName, [Extension.Cjs, Extension.Cts, Extension.Mjs, Extension.Mts]))) && !file.isDeclarationFile ? true : undefined; -} - -/** @internal */ -export function getSetExternalModuleIndicator(options: CompilerOptions): (file: SourceFile) => void { - // TODO: Should this callback be cached? - switch (getEmitModuleDetectionKind(options)) { - case ModuleDetectionKind.Force: - // All non-declaration files are modules, declaration files still do the usual isFileProbablyExternalModule - return (file: SourceFile) => { - file.externalModuleIndicator = isFileProbablyExternalModule(file) || !file.isDeclarationFile || undefined; - }; - case ModuleDetectionKind.Legacy: - // Files are modules if they have imports, exports, or import.meta - return (file: SourceFile) => { - file.externalModuleIndicator = isFileProbablyExternalModule(file); - }; - case ModuleDetectionKind.Auto: - // If module is nodenext or node16, all esm format files are modules - // If jsx is react-jsx or react-jsxdev then jsx tags force module-ness - // otherwise, the presence of import or export statments (or import.meta) implies module-ness - const checks: ((file: SourceFile) => Node | true | undefined)[] = [isFileProbablyExternalModule]; - if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) { - checks.push(isFileModuleFromUsingJSXTag); - } - checks.push(isFileForcedToBeModuleByFormat); - const combined = or(...checks); - const callback = (file: SourceFile) => void (file.externalModuleIndicator = combined(file)); - return callback; - } -} - /** @internal */ export function getEmitScriptTarget(compilerOptions: {module?: CompilerOptions["module"], target?: CompilerOptions["target"]}): ScriptTarget { return compilerOptions.target ?? @@ -8570,21 +7964,6 @@ export function getUseDefineForClassFields(compilerOptions: CompilerOptions): bo return compilerOptions.useDefineForClassFields === undefined ? getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022 : compilerOptions.useDefineForClassFields; } -/** @internal */ -export function compilerOptionsAffectSemanticDiagnostics(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean { - return optionsHaveChanges(oldOptions, newOptions, semanticDiagnosticsOptionDeclarations); -} - -/** @internal */ -export function compilerOptionsAffectEmit(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean { - return optionsHaveChanges(oldOptions, newOptions, affectsEmitOptionDeclarations); -} - -/** @internal */ -export function compilerOptionsAffectDeclarationPath(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean { - return optionsHaveChanges(oldOptions, newOptions, affectsDeclarationPathOptionDeclarations); -} - /** @internal */ export function getCompilerOptionValue(options: CompilerOptions, option: CommandLineOption): unknown { return option.strictFlag ? getStrictOptionValue(options, option.name as StrictOptionName) : options[option.name]; @@ -8630,110 +8009,6 @@ export function hasZeroOrOneAsteriskCharacter(str: string): boolean { return true; } -/** @internal */ -export interface SymlinkedDirectory { - /** Matches the casing returned by `realpath`. Used to compute the `realpath` of children. */ - real: string; - /** toPath(real). Stored to avoid repeated recomputation. */ - realPath: Path; -} - -/** @internal */ -export interface SymlinkCache { - /** Gets a map from symlink to realpath. Keys have trailing directory separators. */ - getSymlinkedDirectories(): ReadonlyMap | undefined; - /** Gets a map from realpath to symlinks. Keys have trailing directory separators. */ - getSymlinkedDirectoriesByRealpath(): MultiMap | undefined; - /** Gets a map from symlink to realpath */ - getSymlinkedFiles(): ReadonlyMap | undefined; - setSymlinkedDirectory(symlink: string, real: SymlinkedDirectory | false): void; - setSymlinkedFile(symlinkPath: Path, real: string): void; - /** - * @internal - * Uses resolvedTypeReferenceDirectives from program instead of from files, since files - * don't include automatic type reference directives. Must be called only when - * `hasProcessedResolutions` returns false (once per cache instance). - */ - setSymlinksFromResolutions(files: readonly SourceFile[], typeReferenceDirectives: ModeAwareCache): void; - /** - * @internal - * Whether `setSymlinksFromResolutions` has already been called. - */ - hasProcessedResolutions(): boolean; -} - -/** @internal */ -export function createSymlinkCache(cwd: string, getCanonicalFileName: GetCanonicalFileName): SymlinkCache { - let symlinkedDirectories: Map | undefined; - let symlinkedDirectoriesByRealpath: MultiMap | undefined; - let symlinkedFiles: Map | undefined; - let hasProcessedResolutions = false; - return { - getSymlinkedFiles: () => symlinkedFiles, - getSymlinkedDirectories: () => symlinkedDirectories, - getSymlinkedDirectoriesByRealpath: () => symlinkedDirectoriesByRealpath, - setSymlinkedFile: (path, real) => (symlinkedFiles || (symlinkedFiles = new Map())).set(path, real), - setSymlinkedDirectory: (symlink, real) => { - // Large, interconnected dependency graphs in pnpm will have a huge number of symlinks - // where both the realpath and the symlink path are inside node_modules/.pnpm. Since - // this path is never a candidate for a module specifier, we can ignore it entirely. - let symlinkPath = toPath(symlink, cwd, getCanonicalFileName); - if (!containsIgnoredPath(symlinkPath)) { - symlinkPath = ensureTrailingDirectorySeparator(symlinkPath); - if (real !== false && !symlinkedDirectories?.has(symlinkPath)) { - (symlinkedDirectoriesByRealpath ||= createMultiMap()).add(ensureTrailingDirectorySeparator(real.realPath), symlink); - } - (symlinkedDirectories || (symlinkedDirectories = new Map())).set(symlinkPath, real); - } - }, - setSymlinksFromResolutions(files, typeReferenceDirectives) { - Debug.assert(!hasProcessedResolutions); - hasProcessedResolutions = true; - for (const file of files) { - file.resolvedModules?.forEach(resolution => processResolution(this, resolution.resolvedModule)); - file.resolvedTypeReferenceDirectiveNames?.forEach(resolution => processResolution(this, resolution.resolvedTypeReferenceDirective)); - } - typeReferenceDirectives.forEach(resolution => processResolution(this, resolution.resolvedTypeReferenceDirective)); - }, - hasProcessedResolutions: () => hasProcessedResolutions, - }; - - function processResolution(cache: SymlinkCache, resolution: ResolvedModuleFull | ResolvedTypeReferenceDirective | undefined) { - if (!resolution || !resolution.originalPath || !resolution.resolvedFileName) return; - const { resolvedFileName, originalPath } = resolution; - cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedFileName); - const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedFileName, originalPath, cwd, getCanonicalFileName) || emptyArray; - if (commonResolved && commonOriginal) { - cache.setSymlinkedDirectory( - commonOriginal, - { real: commonResolved, realPath: toPath(commonResolved, cwd, getCanonicalFileName) }); - } - } -} - -function guessDirectorySymlink(a: string, b: string, cwd: string, getCanonicalFileName: GetCanonicalFileName): [string, string] | undefined { - const aParts = getPathComponents(getNormalizedAbsolutePath(a, cwd)); - const bParts = getPathComponents(getNormalizedAbsolutePath(b, cwd)); - let isDirectory = false; - while ( - aParts.length >= 2 && bParts.length >= 2 && - !isNodeModulesOrScopedPackageDirectory(aParts[aParts.length - 2], getCanonicalFileName) && - !isNodeModulesOrScopedPackageDirectory(bParts[bParts.length - 2], getCanonicalFileName) && - getCanonicalFileName(aParts[aParts.length - 1]) === getCanonicalFileName(bParts[bParts.length - 1]) - ) { - aParts.pop(); - bParts.pop(); - isDirectory = true; - } - return isDirectory ? [getPathFromPathComponents(aParts), getPathFromPathComponents(bParts)] : undefined; -} - -// KLUDGE: Don't assume one 'node_modules' links to another. More likely a single directory inside the node_modules is the symlink. -// ALso, don't assume that an `@foo` directory is linked. More likely the contents of that are linked. -function isNodeModulesOrScopedPackageDirectory(s: string | undefined, getCanonicalFileName: GetCanonicalFileName): boolean { - return s !== undefined && (getCanonicalFileName(s) === "node_modules" || startsWith(s, "@")); -} - function stripLeadingDirectorySeparator(s: string): string | undefined { return isAnyDirectorySeparator(s.charCodeAt(0)) ? s.slice(1) : undefined; } @@ -8744,341 +8019,6 @@ export function tryRemoveDirectoryPrefix(path: string, dirPath: string, getCanon return withoutPrefix === undefined ? undefined : stripLeadingDirectorySeparator(withoutPrefix); } -// Reserved characters, forces escaping of any non-word (or digit), non-whitespace character. -// It may be inefficient (we could just match (/[-[\]{}()*+?.,\\^$|#\s]/g), but this is future -// proof. -const reservedCharacterPattern = /[^\w\s\/]/g; - -/** @internal */ -export function regExpEscape(text: string) { - return text.replace(reservedCharacterPattern, escapeRegExpCharacter); -} - -function escapeRegExpCharacter(match: string) { - return "\\" + match; -} - -const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question]; - -/** @internal */ -export const commonPackageFolders: readonly string[] = ["node_modules", "bower_components", "jspm_packages"]; - -const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`; - -interface WildcardMatcher { - singleAsteriskRegexFragment: string; - doubleAsteriskRegexFragment: string; - replaceWildcardCharacter: (match: string) => string; -} - -const filesMatcher: WildcardMatcher = { - /** - * Matches any single directory segment unless it is the last segment and a .min.js file - * Breakdown: - * [^./] # matches everything up to the first . character (excluding directory separators) - * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension - */ - singleAsteriskRegexFragment: "([^./]|(\\.(?!min\\.js$))?)*", - /** - * Regex for the ** wildcard. Matches any number of subdirectories. When used for including - * files or directories, does not match subdirectories that start with a . character - */ - doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`, - replaceWildcardCharacter: match => replaceWildcardCharacter(match, filesMatcher.singleAsteriskRegexFragment) -}; - -const directoriesMatcher: WildcardMatcher = { - singleAsteriskRegexFragment: "[^/]*", - /** - * Regex for the ** wildcard. Matches any number of subdirectories. When used for including - * files or directories, does not match subdirectories that start with a . character - */ - doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`, - replaceWildcardCharacter: match => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment) -}; - -const excludeMatcher: WildcardMatcher = { - singleAsteriskRegexFragment: "[^/]*", - doubleAsteriskRegexFragment: "(/.+?)?", - replaceWildcardCharacter: match => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment) -}; - -const wildcardMatchers = { - files: filesMatcher, - directories: directoriesMatcher, - exclude: excludeMatcher -}; - -/** @internal */ -export function getRegularExpressionForWildcard(specs: readonly string[] | undefined, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined { - const patterns = getRegularExpressionsForWildcards(specs, basePath, usage); - if (!patterns || !patterns.length) { - return undefined; - } - - const pattern = patterns.map(pattern => `(${pattern})`).join("|"); - // If excluding, match "foo/bar/baz...", but if including, only allow "foo". - const terminator = usage === "exclude" ? "($|/)" : "$"; - return `^(${pattern})${terminator}`; -} - -/** @internal */ -export function getRegularExpressionsForWildcards(specs: readonly string[] | undefined, basePath: string, usage: "files" | "directories" | "exclude"): readonly string[] | undefined { - if (specs === undefined || specs.length === 0) { - return undefined; - } - - return flatMap(specs, spec => - spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage])); -} - -/** - * An "includes" path "foo" is implicitly a glob "foo/** /*" (without the space) if its last component has no extension, - * and does not contain any glob characters itself. - * - * @internal - */ -export function isImplicitGlob(lastPathComponent: string): boolean { - return !/[.*?]/.test(lastPathComponent); -} - -/** @internal */ -export function getPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude") { - const pattern = spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]); - return pattern && `^(${pattern})${usage === "exclude" ? "($|/)" : "$"}`; -} - -function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined { - let subpattern = ""; - let hasWrittenComponent = false; - const components = getNormalizedPathComponents(spec, basePath); - const lastComponent = last(components); - if (usage !== "exclude" && lastComponent === "**") { - return undefined; - } - - // getNormalizedPathComponents includes the separator for the root component. - // We need to remove to create our regex correctly. - components[0] = removeTrailingDirectorySeparator(components[0]); - - if (isImplicitGlob(lastComponent)) { - components.push("**", "*"); - } - - let optionalCount = 0; - for (let component of components) { - if (component === "**") { - subpattern += doubleAsteriskRegexFragment; - } - else { - if (usage === "directories") { - subpattern += "("; - optionalCount++; - } - - if (hasWrittenComponent) { - subpattern += directorySeparator; - } - - if (usage !== "exclude") { - let componentPattern = ""; - // The * and ? wildcards should not match directories or files that start with . if they - // appear first in a component. Dotted directories and files can be included explicitly - // like so: **/.*/.* - if (component.charCodeAt(0) === CharacterCodes.asterisk) { - componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?"; - component = component.substr(1); - } - else if (component.charCodeAt(0) === CharacterCodes.question) { - componentPattern += "[^./]"; - component = component.substr(1); - } - - componentPattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); - - // Patterns should not include subfolders like node_modules unless they are - // explicitly included as part of the path. - // - // As an optimization, if the component pattern is the same as the component, - // then there definitely were no wildcard characters and we do not need to - // add the exclusion pattern. - if (componentPattern !== component) { - subpattern += implicitExcludePathRegexPattern; - } - - subpattern += componentPattern; - } - else { - subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); - } - } - - hasWrittenComponent = true; - } - - while (optionalCount > 0) { - subpattern += ")?"; - optionalCount--; - } - - return subpattern; -} - -function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: string) { - return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match; -} - -/** @internal */ -export interface FileSystemEntries { - readonly files: readonly string[]; - readonly directories: readonly string[]; -} - -/** @internal */ -export interface FileMatcherPatterns { - /** One pattern for each "include" spec. */ - includeFilePatterns: readonly string[] | undefined; - /** One pattern matching one of any of the "include" specs. */ - includeFilePattern: string | undefined; - includeDirectoryPattern: string | undefined; - excludePattern: string | undefined; - basePaths: readonly string[]; -} - -/** - * @param path directory of the tsconfig.json - * - * @internal - */ -export function getFileMatcherPatterns(path: string, excludes: readonly string[] | undefined, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns { - path = normalizePath(path); - currentDirectory = normalizePath(currentDirectory); - const absolutePath = combinePaths(currentDirectory, path); - - return { - includeFilePatterns: map(getRegularExpressionsForWildcards(includes, absolutePath, "files"), pattern => `^${pattern}$`), - includeFilePattern: getRegularExpressionForWildcard(includes, absolutePath, "files"), - includeDirectoryPattern: getRegularExpressionForWildcard(includes, absolutePath, "directories"), - excludePattern: getRegularExpressionForWildcard(excludes, absolutePath, "exclude"), - basePaths: getBasePaths(path, includes, useCaseSensitiveFileNames) - }; -} - -/** @internal */ -export function getRegexFromPattern(pattern: string, useCaseSensitiveFileNames: boolean): RegExp { - return new RegExp(pattern, useCaseSensitiveFileNames ? "" : "i"); -} - -/** - * @param path directory of the tsconfig.json - * - * @internal - */ -export function matchFiles(path: string, extensions: readonly string[] | undefined, excludes: readonly string[] | undefined, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries, realpath: (path: string) => string): string[] { - path = normalizePath(path); - currentDirectory = normalizePath(currentDirectory); - - const patterns = getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames, currentDirectory); - - const includeFileRegexes = patterns.includeFilePatterns && patterns.includeFilePatterns.map(pattern => getRegexFromPattern(pattern, useCaseSensitiveFileNames)); - const includeDirectoryRegex = patterns.includeDirectoryPattern && getRegexFromPattern(patterns.includeDirectoryPattern, useCaseSensitiveFileNames); - const excludeRegex = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, useCaseSensitiveFileNames); - - // Associate an array of results with each include regex. This keeps results in order of the "include" order. - // If there are no "includes", then just put everything in results[0]. - const results: string[][] = includeFileRegexes ? includeFileRegexes.map(() => []) : [[]]; - const visited = new Map(); - const toCanonical = createGetCanonicalFileName(useCaseSensitiveFileNames); - for (const basePath of patterns.basePaths) { - visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth); - } - - return flatten(results); - - function visitDirectory(path: string, absolutePath: string, depth: number | undefined) { - const canonicalPath = toCanonical(realpath(absolutePath)); - if (visited.has(canonicalPath)) return; - visited.set(canonicalPath, true); - const { files, directories } = getFileSystemEntries(path); - - for (const current of sort(files, compareStringsCaseSensitive)) { - const name = combinePaths(path, current); - const absoluteName = combinePaths(absolutePath, current); - if (extensions && !fileExtensionIsOneOf(name, extensions)) continue; - if (excludeRegex && excludeRegex.test(absoluteName)) continue; - if (!includeFileRegexes) { - results[0].push(name); - } - else { - const includeIndex = findIndex(includeFileRegexes, re => re.test(absoluteName)); - if (includeIndex !== -1) { - results[includeIndex].push(name); - } - } - } - - if (depth !== undefined) { - depth--; - if (depth === 0) { - return; - } - } - - for (const current of sort(directories, compareStringsCaseSensitive)) { - const name = combinePaths(path, current); - const absoluteName = combinePaths(absolutePath, current); - if ((!includeDirectoryRegex || includeDirectoryRegex.test(absoluteName)) && - (!excludeRegex || !excludeRegex.test(absoluteName))) { - visitDirectory(name, absoluteName, depth); - } - } - } -} - -/** - * Computes the unique non-wildcard base paths amongst the provided include patterns. - */ -function getBasePaths(path: string, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean): string[] { - // Storage for our results in the form of literal paths (e.g. the paths as written by the user). - const basePaths: string[] = [path]; - - if (includes) { - // Storage for literal base paths amongst the include patterns. - const includeBasePaths: string[] = []; - for (const include of includes) { - // We also need to check the relative paths by converting them to absolute and normalizing - // in case they escape the base path (e.g "..\somedirectory") - const absolute: string = isRootedDiskPath(include) ? include : normalizePath(combinePaths(path, include)); - // Append the literal and canonical candidate base paths. - includeBasePaths.push(getIncludeBasePath(absolute)); - } - - // Sort the offsets array using either the literal or canonical path representations. - includeBasePaths.sort(getStringComparer(!useCaseSensitiveFileNames)); - - // Iterate over each include base path and include unique base paths that are not a - // subpath of an existing base path - for (const includeBasePath of includeBasePaths) { - if (every(basePaths, basePath => !containsPath(basePath, includeBasePath, path, !useCaseSensitiveFileNames))) { - basePaths.push(includeBasePath); - } - } - } - - return basePaths; -} - -function getIncludeBasePath(absolute: string): string { - const wildcardOffset = indexOfAnyCharCode(absolute, wildcardCharCodes); - if (wildcardOffset < 0) { - // No "*" or "?" in the path - return !hasExtension(absolute) - ? absolute - : removeTrailingDirectorySeparator(getDirectoryPath(absolute)); - } - return absolute.substring(0, absolute.lastIndexOf(directorySeparator, wildcardOffset)); -} - /** @internal */ export function ensureScriptKind(fileName: string, scriptKind: ScriptKind | undefined): ScriptKind { // Using scriptKind as a condition handles both: @@ -9113,181 +8053,20 @@ export function getScriptKindFromFileName(fileName: string): ScriptKind { } } -/** - * Groups of supported extensions in order of file resolution precedence. (eg, TS > TSX > DTS and seperately, CTS > DCTS) - * - * @internal - */ -export const supportedTSExtensions: readonly Extension[][] = [[Extension.Ts, Extension.Tsx, Extension.Dts], [Extension.Cts, Extension.Dcts], [Extension.Mts, Extension.Dmts]]; -/** @internal */ -export const supportedTSExtensionsFlat: readonly Extension[] = flatten(supportedTSExtensions); -const supportedTSExtensionsWithJson: readonly Extension[][] = [...supportedTSExtensions, [Extension.Json]]; -/** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */ -const supportedTSExtensionsForExtractExtension: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts, Extension.Cts, Extension.Mts, Extension.Ts, Extension.Tsx]; -/** @internal */ -export const supportedJSExtensions: readonly Extension[][] = [[Extension.Js, Extension.Jsx], [Extension.Mjs], [Extension.Cjs]]; -/** @internal */ -export const supportedJSExtensionsFlat: readonly Extension[] = flatten(supportedJSExtensions); -const allSupportedExtensions: readonly Extension[][] = [[Extension.Ts, Extension.Tsx, Extension.Dts, Extension.Js, Extension.Jsx], [Extension.Cts, Extension.Dcts, Extension.Cjs], [Extension.Mts, Extension.Dmts, Extension.Mjs]]; -const allSupportedExtensionsWithJson: readonly Extension[][] = [...allSupportedExtensions, [Extension.Json]]; -/** @internal */ -export const supportedDeclarationExtensions: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts]; -/** @internal */ -export const supportedTSImplementationExtensions: readonly Extension[] = [Extension.Ts, Extension.Cts, Extension.Mts, Extension.Tsx]; -/** @internal */ -export const extensionsNotSupportingExtensionlessResolution: readonly Extension[] = [Extension.Mts, Extension.Dmts, Extension.Mjs, Extension.Cts, Extension.Dcts, Extension.Cjs]; - -/** @internal */ -export function getSupportedExtensions(options?: CompilerOptions): readonly Extension[][]; -/** @internal */ -export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]): readonly string[][]; -/** @internal */ -export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]): readonly string[][] { - const needJsExtensions = options && getAllowJSCompilerOption(options); - - if (!extraFileExtensions || extraFileExtensions.length === 0) { - return needJsExtensions ? allSupportedExtensions : supportedTSExtensions; - } - - const builtins = needJsExtensions ? allSupportedExtensions : supportedTSExtensions; - const flatBuiltins = flatten(builtins); - const extensions = [ - ...builtins, - ...mapDefined(extraFileExtensions, x => x.scriptKind === ScriptKind.Deferred || needJsExtensions && isJSLike(x.scriptKind) && flatBuiltins.indexOf(x.extension as Extension) === -1 ? [x.extension] : undefined) - ]; - - return extensions; -} - /** @internal */ -export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly Extension[][]): readonly Extension[][]; -/** @internal */ -export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly string[][]): readonly string[][]; -/** @internal */ -export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly string[][]): readonly string[][] { - if (!options || !getResolveJsonModule(options)) return supportedExtensions; - if (supportedExtensions === allSupportedExtensions) return allSupportedExtensionsWithJson; - if (supportedExtensions === supportedTSExtensions) return supportedTSExtensionsWithJson; - return [...supportedExtensions, [Extension.Json]]; -} - -function isJSLike(scriptKind: ScriptKind | undefined): boolean { +export function isJSLike(scriptKind: ScriptKind | undefined): boolean { return scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX; } -/** @internal */ -export function hasJSFileExtension(fileName: string): boolean { - return some(supportedJSExtensionsFlat, extension => fileExtensionIs(fileName, extension)); -} - -/** @internal */ -export function hasTSFileExtension(fileName: string): boolean { - return some(supportedTSExtensionsFlat, extension => fileExtensionIs(fileName, extension)); -} - /** - * @internal - * Corresponds to UserPreferences#importPathEnding - */ -export const enum ModuleSpecifierEnding { - Minimal, - Index, - JsExtension, - TsExtension, -} - -/** @internal */ -export function usesExtensionsOnImports({ imports }: SourceFile, hasExtension: (text: string) => boolean = or(hasJSFileExtension, hasTSFileExtension)): boolean { - return firstDefined(imports, ({ text }) => pathIsRelative(text) && !fileExtensionIsOneOf(text, extensionsNotSupportingExtensionlessResolution) - ? hasExtension(text) - : undefined) || false; -} - -/** @internal */ -export function getModuleSpecifierEndingPreference(preference: UserPreferences["importModuleSpecifierEnding"], resolutionMode: ResolutionMode, compilerOptions: CompilerOptions, sourceFile: SourceFile): ModuleSpecifierEnding { - if (preference === "js" || resolutionMode === ModuleKind.ESNext) { - // Extensions are explicitly requested or required. Now choose between .js and .ts. - if (!shouldAllowImportingTsExtension(compilerOptions)) { - return ModuleSpecifierEnding.JsExtension; - } - // `allowImportingTsExtensions` is a strong signal, so use .ts unless the file - // already uses .js extensions and no .ts extensions. - return inferPreference() !== ModuleSpecifierEnding.JsExtension - ? ModuleSpecifierEnding.TsExtension - : ModuleSpecifierEnding.JsExtension; - } - if (preference === "minimal") { - return ModuleSpecifierEnding.Minimal; - } - if (preference === "index") { - return ModuleSpecifierEnding.Index; - } - - // No preference was specified. - // Look at imports and/or requires to guess whether .js, .ts, or extensionless imports are preferred. - // N.B. that `Index` detection is not supported since it would require file system probing to do - // accurately, and more importantly, literally nobody wants `Index` and its existence is a mystery. - if (!shouldAllowImportingTsExtension(compilerOptions)) { - // If .ts imports are not valid, we only need to see one .js import to go with that. - return usesExtensionsOnImports(sourceFile) ? ModuleSpecifierEnding.JsExtension : ModuleSpecifierEnding.Minimal; - } - - return inferPreference(); - - function inferPreference() { - let usesJsExtensions = false; - const specifiers = sourceFile.imports.length ? sourceFile.imports.map(i => i.text) : - isSourceFileJS(sourceFile) ? getRequiresAtTopOfFile(sourceFile).map(r => r.arguments[0].text) : - emptyArray; - for (const specifier of specifiers) { - if (pathIsRelative(specifier)) { - if (fileExtensionIsOneOf(specifier, extensionsNotSupportingExtensionlessResolution)) { - // These extensions are not optional, so do not indicate a preference. - continue; - } - if (hasTSFileExtension(specifier)) { - return ModuleSpecifierEnding.TsExtension; - } - if (hasJSFileExtension(specifier)) { - usesJsExtensions = true; - } - } - } - return usesJsExtensions ? ModuleSpecifierEnding.JsExtension : ModuleSpecifierEnding.Minimal; - } -} - -function getRequiresAtTopOfFile(sourceFile: SourceFile): readonly RequireOrImportCall[] { - let nonRequireStatementCount = 0; - let requires: RequireOrImportCall[] | undefined; - for (const statement of sourceFile.statements) { - if (nonRequireStatementCount > 3) { - break; - } - if (isRequireVariableStatement(statement)) { - requires = concatenate(requires, statement.declarationList.declarations.map(d => d.initializer)); - } - else if (isExpressionStatement(statement) && isRequireCall(statement.expression, /*requireStringLiteralLikeArgument*/ true)) { - requires = append(requires, statement.expression); - } - else { - nonRequireStatementCount++; - } - } - return requires || emptyArray; -} - -/** @internal */ -export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]) { - if (!fileName) return false; - - const supportedExtensions = getSupportedExtensions(compilerOptions, extraFileExtensions); - for (const extension of flatten(getSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions, supportedExtensions))) { - if (fileExtensionIs(fileName, extension)) { - return true; - } - } - return false; + * @internal + * Corresponds to UserPreferences#importPathEnding + */ +export const enum ModuleSpecifierEnding { + Minimal, + Index, + JsExtension, + TsExtension, } function numberOfDirectorySeparators(str: string) { @@ -9303,33 +8082,6 @@ export function compareNumberOfDirectorySeparators(path1: string, path2: string) ); } -const extensionsToRemove = [Extension.Dts, Extension.Dmts, Extension.Dcts, Extension.Mjs, Extension.Mts, Extension.Cjs, Extension.Cts, Extension.Ts, Extension.Js, Extension.Tsx, Extension.Jsx, Extension.Json]; -/** @internal */ -export function removeFileExtension(path: string): string { - for (const ext of extensionsToRemove) { - const extensionless = tryRemoveExtension(path, ext); - if (extensionless !== undefined) { - return extensionless; - } - } - return path; -} - -/** @internal */ -export function tryRemoveExtension(path: string, extension: string): string | undefined { - return fileExtensionIs(path, extension) ? removeExtension(path, extension) : undefined; -} - -/** @internal */ -export function removeExtension(path: string, extension: string): string { - return path.substring(0, path.length - extension.length); -} - -/** @internal */ -export function changeExtension(path: T, newExtension: string): T { - return changeAnyExtension(path, newExtension, extensionsToRemove, /*ignoreCase*/ false) as T; -} - /** * Returns the input if there are no stars, a pattern if there is exactly one, * and undefined if there are more. @@ -9354,60 +8106,11 @@ export function tryParsePatterns(paths: MapLike): (string | Pattern)[] return mapDefined(getOwnKeys(paths), path => tryParsePattern(path)); } -/** @internal */ -export function positionIsSynthesized(pos: number): boolean { - // This is a fast way of testing the following conditions: - // pos === undefined || pos === null || isNaN(pos) || pos < 0; - return !(pos >= 0); -} - -/** - * True if an extension is one of the supported TypeScript extensions. - * - * @internal - */ -export function extensionIsTS(ext: string): boolean { - return ext === Extension.Ts || ext === Extension.Tsx || ext === Extension.Dts || ext === Extension.Cts || ext === Extension.Mts || ext === Extension.Dmts || ext === Extension.Dcts || (startsWith(ext, ".d.") && endsWith(ext, ".ts")); -} - -/** @internal */ -export function resolutionExtensionIsTSOrJson(ext: string) { - return extensionIsTS(ext) || ext === Extension.Json; -} - -/** - * Gets the extension from a path. - * Path must have a valid extension. - * - * @internal - */ -export function extensionFromPath(path: string): Extension { - const ext = tryGetExtensionFromPath(path); - return ext !== undefined ? ext : Debug.fail(`File ${path} has unknown extension.`); -} - -/** @internal */ -export function isAnySupportedFileExtension(path: string): boolean { - return tryGetExtensionFromPath(path) !== undefined; -} - -/** @internal */ -export function tryGetExtensionFromPath(path: string): Extension | undefined { - return find(extensionsToRemove, e => fileExtensionIs(path, e)); -} - /** @internal */ export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) { return sourceFile.checkJsDirective ? sourceFile.checkJsDirective.enabled : compilerOptions.checkJs; } -/** @internal */ -export const emptyFileSystemEntries: FileSystemEntries = { - files: emptyArray, - directories: emptyArray -}; - - /** * patternOrStrings contains both patterns (containing "*") and regular strings. * Return an exact match if possible, or a pattern match, or undefined. @@ -9430,9 +8133,6 @@ export function matchPatternOrExact(patternOrStrings: readonly (string | Pattern return findBestPatternMatch(patterns, _ => _, candidate); } -/** @internal */ -export type Mutable = { -readonly [K in keyof T]: T[K] }; - /** @internal */ export function sliceAfter(arr: readonly T[], value: T): readonly T[] { const index = arr.indexOf(value); @@ -9503,79 +8203,6 @@ export function isJsonEqual(a: unknown, b: unknown): boolean { return a === b || typeof a === "object" && a !== null && typeof b === "object" && b !== null && equalOwnProperties(a as MapLike, b as MapLike, isJsonEqual); } -/** - * Converts a bigint literal string, e.g. `0x1234n`, - * to its decimal string representation, e.g. `4660`. - * - * @internal - */ -export function parsePseudoBigInt(stringValue: string): string { - let log2Base: number; - switch (stringValue.charCodeAt(1)) { // "x" in "0x123" - case CharacterCodes.b: - case CharacterCodes.B: // 0b or 0B - log2Base = 1; - break; - case CharacterCodes.o: - case CharacterCodes.O: // 0o or 0O - log2Base = 3; - break; - case CharacterCodes.x: - case CharacterCodes.X: // 0x or 0X - log2Base = 4; - break; - default: // already in decimal; omit trailing "n" - const nIndex = stringValue.length - 1; - // Skip leading 0s - let nonZeroStart = 0; - while (stringValue.charCodeAt(nonZeroStart) === CharacterCodes._0) { - nonZeroStart++; - } - return stringValue.slice(nonZeroStart, nIndex) || "0"; - } - - // Omit leading "0b", "0o", or "0x", and trailing "n" - const startIndex = 2, endIndex = stringValue.length - 1; - const bitsNeeded = (endIndex - startIndex) * log2Base; - // Stores the value specified by the string as a LE array of 16-bit integers - // using Uint16 instead of Uint32 so combining steps can use bitwise operators - const segments = new Uint16Array((bitsNeeded >>> 4) + (bitsNeeded & 15 ? 1 : 0)); - // Add the digits, one at a time - for (let i = endIndex - 1, bitOffset = 0; i >= startIndex; i--, bitOffset += log2Base) { - const segment = bitOffset >>> 4; - const digitChar = stringValue.charCodeAt(i); - // Find character range: 0-9 < A-F < a-f - const digit = digitChar <= CharacterCodes._9 - ? digitChar - CharacterCodes._0 - : 10 + digitChar - - (digitChar <= CharacterCodes.F ? CharacterCodes.A : CharacterCodes.a); - const shiftedDigit = digit << (bitOffset & 15); - segments[segment] |= shiftedDigit; - const residual = shiftedDigit >>> 16; - if (residual) segments[segment + 1] |= residual; // overflows segment - } - // Repeatedly divide segments by 10 and add remainder to base10Value - let base10Value = ""; - let firstNonzeroSegment = segments.length - 1; - let segmentsRemaining = true; - while (segmentsRemaining) { - let mod10 = 0; - segmentsRemaining = false; - for (let segment = firstNonzeroSegment; segment >= 0; segment--) { - const newSegment = mod10 << 16 | segments[segment]; - const segmentValue = (newSegment / 10) | 0; - segments[segment] = segmentValue; - mod10 = newSegment - segmentValue * 10; - if (segmentValue && !segmentsRemaining) { - firstNonzeroSegment = segment; - segmentsRemaining = true; - } - } - base10Value = mod10 + base10Value; - } - return base10Value; -} - /** @internal */ export function pseudoBigIntToString({negative, base10Value}: PseudoBigInt): string { return (negative && base10Value !== "0" ? "-" : "") + base10Value; @@ -9741,78 +8368,6 @@ export function setNodeFlags(node: T | undefined, newFlags: Node return node; } -/** - * Bypasses immutability and directly sets the `parent` property of a `Node`. - * - * @internal - */ -export function setParent(child: T, parent: T["parent"] | undefined): T; -/** @internal */ -export function setParent(child: T | undefined, parent: T["parent"] | undefined): T | undefined; -/** @internal */ -export function setParent(child: T | undefined, parent: T["parent"] | undefined): T | undefined { - if (child && parent) { - (child as Mutable).parent = parent; - } - return child; -} - -/** - * Bypasses immutability and directly sets the `parent` property of each `Node` in an array of nodes, if is not already set. - * - * @internal - */ -export function setEachParent(children: T, parent: T[number]["parent"]): T; -/** @internal */ -export function setEachParent(children: T | undefined, parent: T[number]["parent"]): T | undefined; -/** @internal */ -export function setEachParent(children: T | undefined, parent: T[number]["parent"]): T | undefined { - if (children) { - for (const child of children) { - setParent(child, parent); - } - } - return children; -} - -/** - * Bypasses immutability and directly sets the `parent` property of each `Node` recursively. - * @param rootNode The root node from which to start the recursion. - * @param incremental When `true`, only recursively descends through nodes whose `parent` pointers are incorrect. - * This allows us to quickly bail out of setting `parent` for subtrees during incremental parsing. - * - * @internal - */ -export function setParentRecursive(rootNode: T, incremental: boolean): T; -/** @internal */ -export function setParentRecursive(rootNode: T | undefined, incremental: boolean): T | undefined; -/** @internal */ -export function setParentRecursive(rootNode: T | undefined, incremental: boolean): T | undefined { - if (!rootNode) return rootNode; - forEachChildRecursively(rootNode, isJSDocNode(rootNode) ? bindParentToChildIgnoringJSDoc : bindParentToChild); - return rootNode; - - function bindParentToChildIgnoringJSDoc(child: Node, parent: Node): void | "skip" { - if (incremental && child.parent === parent) { - return "skip"; - } - setParent(child, parent); - } - - function bindJSDoc(child: Node) { - if (hasJSDocNodes(child)) { - for (const doc of child.jsDoc!) { - bindParentToChildIgnoringJSDoc(doc, child); - forEachChildRecursively(doc, bindParentToChildIgnoringJSDoc); - } - } - } - - function bindParentToChild(child: Node, parent: Node) { - return bindParentToChildIgnoringJSDoc(child, parent) || bindJSDoc(child); - } -} - function isPackedElement(node: Expression) { return !isOmittedExpression(node); } @@ -9866,11 +8421,6 @@ export function expressionResultIsUnused(node: Expression): boolean { } } -/** @internal */ -export function containsIgnoredPath(path: string) { - return some(ignoredPaths, p => stringContains(path, p)); -} - /** @internal */ export function getContainingNodeArray(node: Node): NodeArray | undefined { if (!node.parent) return undefined; @@ -10014,84 +8564,11 @@ export function isNumericLiteralName(name: string | __String) { return (+name).toString() === name; } -/** @internal */ -export function createPropertyNameNodeForIdentifierOrLiteral(name: string, target: ScriptTarget, singleQuote?: boolean, stringNamed?: boolean) { - return isIdentifierText(name, target) ? factory.createIdentifier(name) : - !stringNamed && isNumericLiteralName(name) && +name >= 0 ? factory.createNumericLiteral(+name) : - factory.createStringLiteral(name, !!singleQuote); -} - /** @internal */ export function isThisTypeParameter(type: Type): boolean { return !!(type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType); } -/** @internal */ -export interface NodeModulePathParts { - readonly topLevelNodeModulesIndex: number; - readonly topLevelPackageNameIndex: number; - readonly packageRootIndex: number; - readonly fileNameIndex: number; -} -/** @internal */ -export function getNodeModulePathParts(fullPath: string): NodeModulePathParts | undefined { - // If fullPath can't be valid module file within node_modules, returns undefined. - // Example of expected pattern: /base/path/node_modules/[@scope/otherpackage/@otherscope/node_modules/]package/[subdirectory/]file.js - // Returns indices: ^ ^ ^ ^ - - let topLevelNodeModulesIndex = 0; - let topLevelPackageNameIndex = 0; - let packageRootIndex = 0; - let fileNameIndex = 0; - - const enum States { - BeforeNodeModules, - NodeModules, - Scope, - PackageContent - } - - let partStart = 0; - let partEnd = 0; - let state = States.BeforeNodeModules; - - while (partEnd >= 0) { - partStart = partEnd; - partEnd = fullPath.indexOf("/", partStart + 1); - switch (state) { - case States.BeforeNodeModules: - if (fullPath.indexOf(nodeModulesPathPart, partStart) === partStart) { - topLevelNodeModulesIndex = partStart; - topLevelPackageNameIndex = partEnd; - state = States.NodeModules; - } - break; - case States.NodeModules: - case States.Scope: - if (state === States.NodeModules && fullPath.charAt(partStart + 1) === "@") { - state = States.Scope; - } - else { - packageRootIndex = partEnd; - state = States.PackageContent; - } - break; - case States.PackageContent: - if (fullPath.indexOf(nodeModulesPathPart, partStart) === partStart) { - state = States.NodeModules; - } - else { - state = States.PackageContent; - } - break; - } - } - - fileNameIndex = partStart; - - return state > States.NodeModules ? { topLevelNodeModulesIndex, topLevelPackageNameIndex, packageRootIndex, fileNameIndex } : undefined; -} - /** @internal */ export function getParameterTypeNode(parameter: ParameterDeclaration | JSDocParameterTag) { return parameter.kind === SyntaxKind.JSDocParameterTag ? parameter.typeExpression?.type : parameter.type; @@ -10119,12 +8596,6 @@ export function isTypeDeclaration(node: Node): node is TypeParameterDeclaration } } -/** @internal */ -export function canHaveExportModifier(node: Node): node is Extract { - return isEnumDeclaration(node) || isVariableStatement(node) || isFunctionDeclaration(node) || isClassDeclaration(node) - || isInterfaceDeclaration(node) || isTypeDeclaration(node) || (isModuleDeclaration(node) && !isExternalModuleAugmentation(node) && !isGlobalScopeAugmentation(node)); -} - /** @internal */ export function isOptionalJSDocPropertyLikeTag(node: Node): node is JSDocPropertyLikeTag { if (!isJSDocPropertyLikeTag(node)) { @@ -10146,11 +8617,6 @@ export function canUsePropertyAccess(name: string, languageVersion: ScriptTarget isIdentifierStart(firstChar, languageVersion); } -/** @internal */ -export function hasTabstop(node: Node): boolean { - return getSnippetElement(node)?.kind === SnippetKind.TabStop; -} - /** @internal */ export function isJSDocOptionalParameter(node: ParameterDeclaration) { return isInJSFile(node) && ( @@ -10176,6 +8642,178 @@ export function isOptionalDeclaration(declaration: Declaration): boolean { } } +/** @internal */ +export function signatureHasRestParameter(s: Signature) { + return !!(s.flags & SignatureFlags.HasRestParameter); +} + +/** @internal */ +export function signatureHasLiteralTypes(s: Signature) { + return !!(s.flags & SignatureFlags.HasLiteralTypes); +} + +/** @internal */ +export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; +/** @internal */ +export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; +/** @internal */ +export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { + while (isOuterExpression(node, kinds)) { + node = node.expression; + } + return node; +} + +/** @internal */ +export function skipAssertions(node: Expression): Expression; +/** @internal */ +export function skipAssertions(node: Node): Node; +/** @internal */ +export function skipAssertions(node: Node): Node { + return skipOuterExpressions(node, OuterExpressionKinds.Assertions); +} + +/** @internal */ +export function isOuterExpression(node: Node, kinds = OuterExpressionKinds.All): node is OuterExpression { + switch (node.kind) { + case SyntaxKind.ParenthesizedExpression: + if (kinds & OuterExpressionKinds.ExcludeJSDocTypeAssertion && isJSDocTypeAssertion(node)) { + return false; + } + return (kinds & OuterExpressionKinds.Parentheses) !== 0; + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.AsExpression: + case SyntaxKind.ExpressionWithTypeArguments: + case SyntaxKind.SatisfiesExpression: + return (kinds & OuterExpressionKinds.TypeAssertions) !== 0; + case SyntaxKind.NonNullExpression: + return (kinds & OuterExpressionKinds.NonNullAssertions) !== 0; + case SyntaxKind.PartiallyEmittedExpression: + return (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) !== 0; + } + return false; +} + +/** @internal */ +export function walkUpOuterExpressions(node: Expression, kinds = OuterExpressionKinds.All): Node { + let parent = node.parent; + while (isOuterExpression(parent, kinds)) { + parent = parent.parent; + Debug.assert(parent); + } + return parent; +} + +/** @internal */ +export function isJSDocTypeAssertion(node: Node): node is JSDocTypeAssertion { + return isParenthesizedExpression(node) && isInJSFile(node) && !!getJSDocTypeTag(node); +} + +/** @internal */ +export function startsWithUseStrict(statements: readonly Statement[]) { + const firstStatement = firstOrUndefined(statements); + return firstStatement !== undefined && isPrologueDirective(firstStatement) && isUseStrictPrologue(firstStatement); +} + +function isUseStrictPrologue(node: ExpressionStatement): boolean { + return isStringLiteral(node.expression) && node.expression.text === "use strict"; +} + +/** @internal */ +export function findUseStrictPrologue(statements: readonly Statement[]): Statement | undefined { + for (const statement of statements) { + if (isPrologueDirective(statement)) { + if (isUseStrictPrologue(statement)) { + return statement; + } + } + else { + break; + } + } + return undefined; +} + +/** @internal */ +export function canHaveIllegalDecorators(node: Node): node is HasIllegalDecorators { + const kind = node.kind; + return kind === SyntaxKind.PropertyAssignment + || kind === SyntaxKind.ShorthandPropertyAssignment + || kind === SyntaxKind.FunctionDeclaration + || kind === SyntaxKind.Constructor + || kind === SyntaxKind.IndexSignature + || kind === SyntaxKind.ClassStaticBlockDeclaration + || kind === SyntaxKind.MissingDeclaration + || kind === SyntaxKind.VariableStatement + || kind === SyntaxKind.InterfaceDeclaration + || kind === SyntaxKind.TypeAliasDeclaration + || kind === SyntaxKind.EnumDeclaration + || kind === SyntaxKind.ModuleDeclaration + || kind === SyntaxKind.ImportEqualsDeclaration + || kind === SyntaxKind.ImportDeclaration + || kind === SyntaxKind.NamespaceExportDeclaration + || kind === SyntaxKind.ExportDeclaration + || kind === SyntaxKind.ExportAssignment; +} + +/** @internal */ +export function canHaveIllegalTypeParameters(node: Node): node is HasIllegalTypeParameters { + const kind = node.kind; + return kind === SyntaxKind.Constructor + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor; +} + +/** @internal */ +export function isModuleAugmentationExternal(node: AmbientModuleDeclaration) { + // external module augmentation is a ambient module declaration that is either: + // - defined in the top level scope and source file is an external module + // - defined inside ambient module declaration located in the top level scope and source file not an external module + switch (node.parent.kind) { + case SyntaxKind.SourceFile: + return isExternalModule(node.parent); + case SyntaxKind.ModuleBlock: + return isAmbientModule(node.parent.parent) && isSourceFile(node.parent.parent.parent) && !isExternalModule(node.parent.parent.parent); + } + return false; +} + +/** @internal */ +export function isExternalModuleAugmentation(node: Node): node is AmbientModuleDeclaration { + return isAmbientModule(node) && isModuleAugmentationExternal(node); +} + +/** @internal */ +export function isAmbientModule(node: Node): node is AmbientModuleDeclaration { + return isModuleDeclaration(node) && (node.name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(node)); +} + +/** @internal */ +export function isGlobalScopeAugmentation(module: ModuleDeclaration): boolean { + return !!(module.flags & NodeFlags.GlobalAugmentation); +} + +/** @internal */ +export function getNonAugmentationDeclaration(symbol: Symbol) { + return symbol.declarations?.find(d => !isExternalModuleAugmentation(d) && !(isModuleDeclaration(d) && isGlobalScopeAugmentation(d))); +} + +/** @internal */ +export function getSourceFileOfModule(module: Symbol) { + return getSourceFileOfNode(module.valueDeclaration || getNonAugmentationDeclaration(module)); +} + +/** @internal */ +export function isEffectiveExternalModule(node: SourceFile, compilerOptions: CompilerOptions) { + return isExternalModule(node) || getIsolatedModules(compilerOptions) || (isCommonJSContainingModuleKind(getEmitModuleKind(compilerOptions)) && !!node.commonJsModuleIndicator); +} + +/** @internal */ +export function canHaveExportModifier(node: Node): node is Extract { + return isEnumDeclaration(node) || isVariableStatement(node) || isFunctionDeclaration(node) || isClassDeclaration(node) + || isInterfaceDeclaration(node) || isTypeDeclaration(node) || (isModuleDeclaration(node) && !isExternalModuleAugmentation(node) && !isGlobalScopeAugmentation(node)); +} + /** @internal */ export function isNonNullAccess(node: Node): node is AccessExpression { const kind = node.kind; diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 3767bcc3f1118..83c444549f55e 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1,3 +1,87 @@ +import { + contains, + emptyArray, + every, + filter, + find, + flatMap, + hasProperty, + isWhiteSpaceLike, + lastOrUndefined, + length, + setUILocale, + some, + sortAndDeduplicate, + tryCast, +} from "./core"; +import { SortedReadonlyArray } from "./corePublic"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { + isArrowFunction, + isBinaryExpression, + isBindingElement, + isBlock, + isCallExpression, + isCallSignatureDeclaration, + isClassExpression, + isClassStaticBlockDeclaration, + isDecorator, + isElementAccessExpression, + isExportAssignment, + isExportDeclaration, + isExportSpecifier, + isFunctionExpression, + isFunctionTypeNode, + isIdentifier, + isImportSpecifier, + isJSDoc, + isJSDocAugmentsTag, + isJSDocClassTag, + isJSDocDeprecatedTag, + isJSDocEnumTag, + isJSDocFunctionType, + isJSDocImplementsTag, + isJSDocOverloadTag, + isJSDocOverrideTag, + isJSDocParameterTag, + isJSDocPrivateTag, + isJSDocProtectedTag, + isJSDocPublicTag, + isJSDocReadonlyTag, + isJSDocReturnTag, + isJSDocSatisfiesTag, + isJSDocSignature, + isJSDocTemplateTag, + isJSDocThisTag, + isJSDocTypeLiteral, + isJSDocTypeTag, + isModuleBlock, + isNonNullExpression, + isNotEmittedStatement, + isOmittedExpression, + isParameter, + isPartiallyEmittedExpression, + isPrivateIdentifier, + isPropertyAccessExpression, + isPropertyAssignment, + isPropertyDeclaration, + isSourceFile, + isStringLiteral, + isTypeLiteralNode, + isTypeReferenceNode, + isVariableDeclaration, + isVariableDeclarationList, + isVariableStatement, +} from "./factory/nodeTests"; +import { + combinePaths, + getDirectoryPath, + isRootedDiskPath, + normalizePath, + pathIsRelative, +} from "./path"; +import { stringToToken } from "./scanner"; import { __String, AccessExpression, @@ -24,44 +108,30 @@ import { CallChain, CallExpression, CallLikeExpression, - canHaveIllegalTypeParameters, - canHaveJSDoc, CaseOrDefaultClause, CharacterCodes, ClassElement, ClassLikeDeclaration, ClassStaticBlockDeclaration, - combinePaths, - compareDiagnostics, CompilerOptions, ConciseBody, ConstructorDeclaration, ConstructorTypeNode, - contains, - createCompilerDiagnostic, - Debug, Declaration, DeclarationName, DeclarationStatement, DeclarationWithTypeParameters, Decorator, Diagnostic, - Diagnostics, ElementAccessChain, ElementAccessExpression, - emptyArray, EntityName, - entityNameToString, EnumDeclaration, - every, ExportAssignment, ExportDeclaration, ExportSpecifier, Expression, FileReference, - filter, - find, - flatMap, ForInitializer, ForInOrOfStatement, FunctionBody, @@ -70,98 +140,18 @@ import { GeneratedIdentifier, GeneratedPrivateIdentifier, GetAccessorDeclaration, - getAssignmentDeclarationKind, - getDirectoryPath, - getEffectiveModifierFlags, - getEffectiveModifierFlagsAlwaysIncludeJSDoc, - getElementOrPropertyAccessArgumentExpressionOrName, - getEmitScriptTarget, - getJSDocCommentsAndTags, - getJSDocRoot, - getJSDocTypeParameterDeclarations, - hasAccessorModifier, HasDecorators, - hasDecorators, HasExpressionInitializer, HasInitializer, HasJSDoc, HasLocals, HasModifiers, - hasProperty, - hasSyntacticModifier, HasType, Identifier, ImportClause, ImportEqualsDeclaration, ImportSpecifier, ImportTypeNode, - isAccessExpression, - isAmbientModule, - isAnyImportOrReExport, - isArrowFunction, - isAssignmentExpression, - isBinaryExpression, - isBindableStaticElementAccessExpression, - isBindingElement, - isBlock, - isCallExpression, - isCallSignatureDeclaration, - isClassExpression, - isClassStaticBlockDeclaration, - isDecorator, - isElementAccessExpression, - isExportAssignment, - isExportDeclaration, - isExportSpecifier, - isFunctionBlock, - isFunctionExpression, - isFunctionTypeNode, - isIdentifier, - isImportSpecifier, - isInJSFile, - isJSDoc, - isJSDocAugmentsTag, - isJSDocClassTag, - isJSDocDeprecatedTag, - isJSDocEnumTag, - isJSDocFunctionType, - isJSDocImplementsTag, - isJSDocOverloadTag, - isJSDocOverrideTag, - isJSDocParameterTag, - isJSDocPrivateTag, - isJSDocProtectedTag, - isJSDocPublicTag, - isJSDocReadonlyTag, - isJSDocReturnTag, - isJSDocSatisfiesTag, - isJSDocSignature, - isJSDocTemplateTag, - isJSDocThisTag, - isJSDocTypeAlias, - isJSDocTypeLiteral, - isJSDocTypeTag, - isKeyword, - isModuleBlock, - isNonNullExpression, - isNotEmittedStatement, - isOmittedExpression, - isParameter, - isPartiallyEmittedExpression, - isPrivateIdentifier, - isPropertyAccessExpression, - isPropertyAssignment, - isPropertyDeclaration, - isRootedDiskPath, - isSourceFile, - isStringLiteral, - isTypeLiteralNode, - isTypeNodeKind, - isTypeReferenceNode, - isVariableDeclaration, - isVariableDeclarationList, - isVariableStatement, - isWhiteSpaceLike, IterationStatement, JSDocAugmentsTag, JSDocClassTag, @@ -196,9 +186,7 @@ import { JsxTagNameExpression, KeywordSyntaxKind, LabeledStatement, - lastOrUndefined, LeftHandSideExpression, - length, LiteralExpression, LiteralToken, MemberName, @@ -206,7 +194,6 @@ import { Modifier, ModifierFlags, ModifierLike, - modifierToFlag, ModuleBody, ModuleDeclaration, ModuleReference, @@ -221,7 +208,6 @@ import { NodeArray, NodeFlags, NonNullChain, - normalizePath, NotEmittedStatement, NullLiteral, ObjectBindingOrAssignmentElement, @@ -233,7 +219,6 @@ import { OuterExpressionKinds, ParameterDeclaration, PartiallyEmittedExpression, - pathIsRelative, PostfixUnaryExpression, PrefixUnaryExpression, PrivateClassElementDeclaration, @@ -246,17 +231,10 @@ import { QualifiedName, ScriptTarget, SetAccessorDeclaration, - setLocalizedDiagnosticMessages, - setUILocale, SignatureDeclaration, - skipOuterExpressions, - some, - sortAndDeduplicate, - SortedReadonlyArray, Statement, StringLiteral, StringLiteralLike, - stringToToken, Symbol, SyntaxKind, TemplateLiteral, @@ -266,7 +244,6 @@ import { TextChangeRange, TextRange, TextSpan, - tryCast, TypeElement, TypeNode, TypeOnlyAliasDeclaration, @@ -278,7 +255,38 @@ import { UnparsedNode, UnparsedTextLike, VariableDeclaration, -} from "./_namespaces/ts"; +} from "./types"; +import { + canHaveIllegalTypeParameters, + canHaveJSDoc, + compareDiagnostics, + createCompilerDiagnostic, + entityNameToString, + getAssignmentDeclarationKind, + getEffectiveModifierFlags, + getEffectiveModifierFlagsAlwaysIncludeJSDoc, + getElementOrPropertyAccessArgumentExpressionOrName, + getEmitScriptTarget, + getJSDocCommentsAndTags, + getJSDocRoot, + getJSDocTypeParameterDeclarations, + hasAccessorModifier, + hasDecorators, + hasSyntacticModifier, + isAccessExpression, + isAmbientModule, + isAnyImportOrReExport, + isAssignmentExpression, + isBindableStaticElementAccessExpression, + isFunctionBlock, + isInJSFile, + isJSDocTypeAlias, + isKeyword, + isTypeNodeKind, + modifierToFlag, + setLocalizedDiagnosticMessages, + skipOuterExpressions, +} from "./utilities"; export function isExternalModuleNameRelative(moduleName: string): boolean { // TypeScript 1.0 spec (April 2014): 11.2.1 diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 08475d5c0ae7a..db7ddb55aa486 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -1,90 +1,64 @@ import { - ConciseBody, - Debug, - EmitFlags, - Expression, - factory, - FunctionBody, - getEmitFlags, - getEmitScriptTarget, - HasChildren, - Identifier, isArray, - isArrayBindingElement, + singleOrUndefined, + some, +} from "./core"; +import * as Debug from "./debug"; +import { setEmitFlags } from "./factory/emitNode"; +import { factory } from "./factory/nodeFactory"; +import { isAssertClause, isAssertEntry, - isAssertionKey, isAssertsKeyword, isAsteriskToken, isAwaitKeyword, - isBinaryOperatorToken, isBindingElement, - isBindingName, - isBindingPattern, isBlock, - isCallChain, isCaseBlock, - isCaseOrDefaultClause, isCatchClause, - isClassElement, isColonToken, - isConciseBody, isDotDotDotToken, - isElementAccessChain, - isEntityName, isEnumMember, isEqualsGreaterThanToken, isExclamationToken, isExportSpecifier, - isExpression, isExpressionWithTypeArguments, - isForInitializer, isHeritageClause, isIdentifier, - isIdentifierOrThisTypeNode, isImportClause, isImportSpecifier, isImportTypeAssertionContainer, - isJsxAttributeLike, - isJsxAttributeName, isJsxAttributes, - isJsxChild, isJsxClosingElement, isJsxClosingFragment, isJsxOpeningElement, isJsxOpeningFragment, - isJsxTagNameExpression, - isLiteralTypeLiteral, - isMemberName, - isModifier, - isModifierLike, - isModuleBody, - isModuleName, - isModuleReference, - isNamedExportBindings, - isNamedImportBindings, - isObjectLiteralElementLike, - isOptionalChain, isParameter, - isPropertyAccessChain, - isPropertyName, isQuestionDotToken, - isQuestionOrExclamationToken, - isQuestionOrPlusOrMinusToken, isQuestionToken, - isReadonlyKeywordOrPlusOrMinusToken, - isStatement, - isStringLiteralOrJsxExpression, isTemplateHead, - isTemplateLiteral, isTemplateLiteralTypeSpan, - isTemplateMiddleOrTemplateTail, isTemplateSpan, - isTypeElement, - isTypeNode, isTypeParameterDeclaration, isVariableDeclaration, isVariableDeclarationList, +} from "./factory/nodeTests"; +import { + isBinaryOperatorToken, + isIdentifierOrThisTypeNode, + isModuleName, + isQuestionOrExclamationToken, + isQuestionOrPlusOrMinusToken, + isReadonlyKeywordOrPlusOrMinusToken, +} from "./factory/utilities"; +import { setTextRange } from "./factory/utilitiesPublic"; +import { + ConciseBody, + EmitFlags, + Expression, + FunctionBody, + HasChildren, + Identifier, LexicalEnvironmentFlags, Node, NodeArray, @@ -92,16 +66,52 @@ import { NodeVisitor, ParameterDeclaration, ScriptTarget, - setEmitFlags, - setTextRange, - setTextRangePosEnd, - singleOrUndefined, - some, Statement, SyntaxKind, TransformationContext, Visitor, -} from "./_namespaces/ts"; +} from "./types"; +import { + getEmitFlags, + getEmitScriptTarget, + isJsxAttributeName, + setTextRangePosEnd, +} from "./utilities"; +import { + isArrayBindingElement, + isAssertionKey, + isBindingName, + isBindingPattern, + isCallChain, + isCaseOrDefaultClause, + isClassElement, + isConciseBody, + isElementAccessChain, + isEntityName, + isExpression, + isForInitializer, + isJsxAttributeLike, + isJsxChild, + isJsxTagNameExpression, + isLiteralTypeLiteral, + isMemberName, + isModifier, + isModifierLike, + isModuleBody, + isModuleReference, + isNamedExportBindings, + isNamedImportBindings, + isObjectLiteralElementLike, + isOptionalChain, + isPropertyAccessChain, + isPropertyName, + isStatement, + isStringLiteralOrJsxExpression, + isTemplateLiteral, + isTemplateMiddleOrTemplateTail, + isTypeElement, + isTypeNode, +} from "./utilitiesPublic"; /** * Visits a Node using the supplied visitor, possibly returning a new Node in its place. diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index a829f95d17fbd..10577640170c0 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -1,112 +1,138 @@ import { - addRange, BuilderProgram, - CancellationToken, - chainDiagnosticMessages, - CharacterCodes, - combinePaths, - CompilerHost, - CompilerOptions, + createEmitAndSemanticDiagnosticsBuilderProgram, + EmitAndSemanticDiagnosticsBuilderProgram, +} from "./builderPublic"; +import { + DiagnosticReporter, + ExtendedConfigCacheEntry, + getParsedCommandLineOfConfigFile, + ParseConfigFileHost, + targetOptionDeclaration, +} from "./commandLineParser"; +import { + addRange, contains, - convertToRelativePath, copyProperties, countWhere, - createCompilerDiagnostic, - createEmitAndSemanticDiagnosticsBuilderProgram, createGetCanonicalFileName, + emptyArray, + endsWith, + filter, + find, + forEach, + isLineBreak, + isString, + last, + maybeBind, + memoize, + noop, +} from "./core"; +import { SortedReadonlyArray } from "./corePublic"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { + getPatternFromSpec, + getRegexFromPattern, +} from "./fileMatcher"; +import { + combinePaths, + convertToRelativePath, + fileExtensionIs, + getDirectoryPath, + getNormalizedAbsolutePath, + getRelativePathFromDirectory, + normalizePath, + pathIsAbsolute, +} from "./path"; +import { createGetSourceFile, - createIncrementalCompilerHost, - createIncrementalProgram, - CreateProgram, createWriteFileMeasuringIO, + flattenDiagnosticMessageText, + ForegroundColorEscapeSequences, + formatColorAndReset, + formatDiagnostic, + FormatDiagnosticsHost, + formatDiagnosticsWithColorAndContext, + getReferencedFileLocation, + isReferencedFile, + isReferenceFileLocation, +} from "./program"; +import { getLineAndCharacterOfPosition } from "./scanner"; +import { + sourceMapCommentRegExp, + sourceMapCommentRegExpDontCareLineStart, + whitespaceOrMapCommentRegExp, +} from "./sourcemap"; +import { + generateDjb2Hash, + sys, +} from "./sys"; +import { + ReportEmitErrorSummary, + ReportFileInError, +} from "./tsbuildPublic"; +import { + CancellationToken, + CharacterCodes, + CompilerHost, + CompilerOptions, CustomTransformers, - Debug, Diagnostic, DiagnosticAndArguments, DiagnosticCategory, DiagnosticMessage, DiagnosticMessageChain, - DiagnosticReporter, - Diagnostics, - DirectoryStructureHost, - EmitAndSemanticDiagnosticsBuilderProgram, EmitResult, - emptyArray, - endsWith, ExitStatus, - ExtendedConfigCacheEntry, Extension, - externalHelpersModuleNameText, FileExtensionInfo, - fileExtensionIs, FileIncludeKind, FileIncludeReason, FileWatcher, - filter, - find, - flattenDiagnosticMessageText, - forEach, - forEachEntry, - ForegroundColorEscapeSequences, - formatColorAndReset, - formatDiagnostic, - FormatDiagnosticsHost, - formatDiagnosticsWithColorAndContext, - generateDjb2Hash, - getDefaultLibFileName, - getDirectoryPath, - getEmitScriptTarget, - getLineAndCharacterOfPosition, - getNewLineCharacter, - getNormalizedAbsolutePath, - getParsedCommandLineOfConfigFile, - getPatternFromSpec, - getReferencedFileLocation, - getRegexFromPattern, - getRelativePathFromDirectory, - getWatchFactory, HasCurrentDirectory, - isExternalOrCommonJsModule, - isLineBreak, - isReferencedFile, - isReferenceFileLocation, - isString, - last, - maybeBind, - memoize, ModuleKind, - noop, - normalizePath, - outFile, - packageIdToString, - ParseConfigFileHost, ParsedCommandLine, - pathIsAbsolute, Program, - ProgramHost, ProjectReference, - ReportEmitErrorSummary, - ReportFileInError, - sortAndDeduplicateDiagnostics, - SortedReadonlyArray, SourceFile, - sourceMapCommentRegExp, - sourceMapCommentRegExpDontCareLineStart, - sys, System, - targetOptionDeclaration, + WatchOptions, + WriteFileCallback, +} from "./types"; +import { + chainDiagnosticMessages, + createCompilerDiagnostic, + externalHelpersModuleNameText, + forEachEntry, + getEmitScriptTarget, + getNewLineCharacter, + isExternalOrCommonJsModule, + outFile, + packageIdToString, +} from "./utilities"; +import { + getDefaultLibFileName, + sortAndDeduplicateDiagnostics, +} from "./utilitiesPublic"; +import { + createIncrementalCompilerHost, + createIncrementalProgram, + CreateProgram, + ProgramHost, WatchCompilerHost, WatchCompilerHostOfConfigFile, WatchCompilerHostOfFilesAndCompilerOptions, + WatchHost, + WatchStatusReporter, +} from "./watchPublic"; +import { + DirectoryStructureHost, + getWatchFactory, WatchFactory, WatchFactoryHost, - WatchHost, WatchLogLevel, - WatchOptions, - WatchStatusReporter, - whitespaceOrMapCommentRegExp, - WriteFileCallback, -} from "./_namespaces/ts"; +} from "./watchUtilities"; const sysFormatDiagnosticsHost: FormatDiagnosticsHost | undefined = sys ? { getCurrentDirectory: () => sys.getCurrentDirectory(), diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index 1dd9ff02a2a9b..93113c55facf0 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -1,98 +1,121 @@ +import { createBuilderProgramUsingProgramBuildInfo } from "./builder"; import { BuilderProgram, - BuildInfo, + createEmitAndSemanticDiagnosticsBuilderProgram, + EmitAndSemanticDiagnosticsBuilderProgram, +} from "./builderPublic"; +import { canJsonReportNoInputFiles, - changeCompilerHostLikeToUseCache, - changesAffectModuleResolution, - cleanExtendedConfigCache, - clearMap, - clearSharedExtendedConfigFileWatcher, - closeFileWatcher, - closeFileWatcherOf, - CompilerHost, - CompilerOptions, ConfigFileDiagnosticsReporter, - ConfigFileProgramReloadLevel, - createBuilderProgramUsingProgramBuildInfo, - createCachedDirectoryStructureHost, - createCompilerDiagnostic, - createCompilerHostFromProgramHost, - createCompilerHostWorker, - createEmitAndSemanticDiagnosticsBuilderProgram, + DiagnosticReporter, + ExtendedConfigCacheEntry, + getFileNamesFromConfigSpecs, + getParsedCommandLineOfConfigFile, + updateErrorForNoInputFiles, +} from "./commandLineParser"; +import { createGetCanonicalFileName, + isArray, + maybeBind, + noop, + returnFalse, + returnTrue, +} from "./core"; +import { + MapLike, + version, +} from "./corePublic"; +import * as Debug from "./debug"; +import { Diagnostics } from "./diagnosticInformationMap.generated"; +import { + getBuildInfo, + getTsBuildInfoEmitOutputFilePath, +} from "./emitter"; +import { ModuleResolutionCache } from "./moduleNameResolver"; +import { CreateSourceFileOptions } from "./parser"; +import { + getDirectoryPath, + getNormalizedAbsolutePath, + toPath, +} from "./path"; +import { perfLogger } from "./perfLogger"; +import { + changeCompilerHostLikeToUseCache, + createCompilerHostWorker, + getConfigFileParsingDiagnostics, + isProgramUptoDate, + parseConfigHostFromCompilerHostLike, +} from "./program"; +import { changesAffectModuleResolution } from "./programUtilities"; +import { createResolutionCache, - CreateSourceFileOptions, - createWatchCompilerHostOfConfigFile, - createWatchCompilerHostOfFilesAndCompilerOptions, - createWatchFactory, - Debug, + ResolutionCacheHost, +} from "./resolutionCache"; +import { + PollingInterval, + sys, +} from "./sys"; +import { + BuildInfo, + CompilerHost, + CompilerOptions, Diagnostic, DiagnosticMessage, - DiagnosticReporter, - Diagnostics, - DirectoryStructureHost, DirectoryWatcherCallback, - EmitAndSemanticDiagnosticsBuilderProgram, - ExtendedConfigCacheEntry, FileExtensionInfo, FileReference, FileWatcher, FileWatcherCallback, FileWatcherEventKind, - getBuildInfo, - getConfigFileParsingDiagnostics, - getDirectoryPath, - getFileNamesFromConfigSpecs, - getNewLineCharacter, - getNormalizedAbsolutePath, - getParsedCommandLineOfConfigFile, - getSourceFileVersionAsHashFromText, - getTsBuildInfoEmitOutputFilePath, HasInvalidatedLibResolutions, HasInvalidatedResolutions, - isArray, - isIgnoredFileFromWildCardWatching, - isProgramUptoDate, - MapLike, - maybeBind, - ModuleResolutionCache, - noop, - noopFileWatcher, - parseConfigHostFromCompilerHostLike, ParsedCommandLine, Path, - perfLogger, - PollingInterval, ProjectReference, - ResolutionCacheHost, ResolutionMode, ResolvedModule, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirective, ResolvedTypeReferenceDirectiveWithFailedLookupLocations, - returnFalse, - returnTrue, ScriptTarget, - setGetSourceFileAsHashVersioned, - SharedExtendedConfigFileWatcher, SourceFile, StringLiteralLike, - sys, System, - toPath, - toPath as ts_toPath, - updateErrorForNoInputFiles, - updateMissingFilePathsWatch, - updateSharedExtendedConfigFileWatcher, - updateWatchingWildcardDirectories, - version, WatchDirectoryFlags, WatchOptions, +} from "./types"; +import { + clearMap, + closeFileWatcher, + createCompilerDiagnostic, + getNewLineCharacter, +} from "./utilities"; +import { + createCompilerHostFromProgramHost, + createWatchCompilerHostOfConfigFile, + createWatchCompilerHostOfFilesAndCompilerOptions, + createWatchFactory, + getSourceFileVersionAsHashFromText, + noopFileWatcher, + setGetSourceFileAsHashVersioned, WatchType, WatchTypeRegistry, +} from "./watch"; +import { + cleanExtendedConfigCache, + clearSharedExtendedConfigFileWatcher, + closeFileWatcherOf, + ConfigFileProgramReloadLevel, + createCachedDirectoryStructureHost, + DirectoryStructureHost, + isIgnoredFileFromWildCardWatching, + SharedExtendedConfigFileWatcher, + updateMissingFilePathsWatch, + updateSharedExtendedConfigFileWatcher, + updateWatchingWildcardDirectories, WildcardDirectoryWatcher, -} from "./_namespaces/ts"; +} from "./watchUtilities"; export interface ReadBuildProgramHost { useCaseSensitiveFileNames(): boolean; @@ -477,15 +500,15 @@ export function createWatchProgram(host: WatchCompiler setGetSourceFileAsHashVersioned(compilerHost); // Members for CompilerHost const getNewSourceFile = compilerHost.getSourceFile; - compilerHost.getSourceFile = (fileName, ...args) => getVersionedSourceFileByPath(fileName, toPath(fileName), ...args); + compilerHost.getSourceFile = (fileName, ...args) => getVersionedSourceFileByPath(fileName, toPathWorker(fileName), ...args); compilerHost.getSourceFileByPath = getVersionedSourceFileByPath; compilerHost.getNewLine = () => newLine; compilerHost.fileExists = fileExists; compilerHost.onReleaseOldSourceFile = onReleaseOldSourceFile; compilerHost.onReleaseParsedCommandLine = onReleaseParsedCommandLine; // Members for ResolutionCacheHost - compilerHost.toPath = toPath; - compilerHost.getCompilationSettings = () => compilerOptions!; + compilerHost.toPath = toPathWorker; + compilerHost.getCompilationSettings = () => compilerOptions as CompilerOptions; compilerHost.useSourceOfProjectReferenceRedirect = maybeBind(host, host.useSourceOfProjectReferenceRedirect); compilerHost.watchDirectoryOfFailedLookupLocation = (dir, cb, flags) => watchDirectory(dir, cb, flags, watchOptions, WatchType.FailedLookupLocations); compilerHost.watchAffectingFileLocation = (file, cb) => watchFile(file, cb, PollingInterval.High, watchOptions, WatchType.AffectingFileLocation); @@ -540,7 +563,7 @@ export function createWatchProgram(host: WatchCompiler watchConfigFileWildCardDirectories(); // Update extended config file watch - if (configFileName) updateExtendedConfigFilesWatches(toPath(configFileName), compilerOptions, watchOptions, WatchType.ExtendedConfigFile); + if (configFileName) updateExtendedConfigFilesWatches(toPathWorker(configFileName), compilerOptions, watchOptions, WatchType.ExtendedConfigFile); return configFileName ? { getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, close } : @@ -613,7 +636,7 @@ export function createWatchProgram(host: WatchCompiler const { originalReadFile, originalFileExists, originalDirectoryExists, originalCreateDirectory, originalWriteFile, readFileWithCache - } = changeCompilerHostLikeToUseCache(compilerHost, toPath); + } = changeCompilerHostLikeToUseCache(compilerHost, toPathWorker); if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, path => getSourceVersion(path, readFileWithCache), fileName => compilerHost.fileExists(fileName), hasInvalidatedResolutions, hasInvalidatedLibResolutions, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) { if (hasChangedConfigFileParsingErrors) { if (reportFileChangeDetectedOnCreateProgram) { @@ -693,8 +716,8 @@ export function createWatchProgram(host: WatchCompiler return getNewLineCharacter(compilerOptions || optionsToExtendForConfigFile); } - function toPath(fileName: string) { - return ts_toPath(fileName, currentDirectory, getCanonicalFileName); + function toPathWorker(fileName: string) { + return toPath(fileName, currentDirectory, getCanonicalFileName); } function isFileMissingOnHost(hostSourceFile: HostFileInfo | undefined): hostSourceFile is FileMissingOnHost { @@ -706,7 +729,7 @@ export function createWatchProgram(host: WatchCompiler } function fileExists(fileName: string) { - const path = toPath(fileName); + const path = toPathWorker(fileName); // If file is missing on host from cache, we can definitely say file doesnt exist // otherwise we need to ensure from the disk if (isFileMissingOnHost(sourceFilesCache.get(path))) { @@ -913,7 +936,7 @@ export function createWatchProgram(host: WatchCompiler watchConfigFileWildCardDirectories(); // Update extended config file watch - updateExtendedConfigFilesWatches(toPath(configFileName), compilerOptions, watchOptions, WatchType.ExtendedConfigFile); + updateExtendedConfigFilesWatches(toPathWorker(configFileName), compilerOptions, watchOptions, WatchType.ExtendedConfigFile); } function parseConfigFile() { @@ -940,7 +963,7 @@ export function createWatchProgram(host: WatchCompiler } function getParsedCommandLine(configFileName: string): ParsedCommandLine | undefined { - const configPath = toPath(configFileName); + const configPath = toPathWorker(configFileName); let config = parsedConfigs?.get(configPath); if (config) { if (!config.reloadLevel) return config.parsedCommandLine; @@ -991,7 +1014,7 @@ export function createWatchProgram(host: WatchCompiler } function onReleaseParsedCommandLine(fileName: string) { - const path = toPath(fileName); + const path = toPathWorker(fileName); const config = parsedConfigs?.get(path); if (!config) return; @@ -1073,7 +1096,7 @@ export function createWatchProgram(host: WatchCompiler Debug.assert(configFileName); Debug.assert(compilerOptions); - const fileOrDirectoryPath = toPath(fileOrDirectory); + const fileOrDirectoryPath = toPathWorker(fileOrDirectory); // Since the file existence changed, update the sourceFiles cache if (cachedDirectoryStructureHost) { @@ -1082,7 +1105,7 @@ export function createWatchProgram(host: WatchCompiler nextSourceFileVersion(fileOrDirectoryPath); if (isIgnoredFileFromWildCardWatching({ - watchedDirPath: toPath(directory), + watchedDirPath: toPathWorker(directory), fileOrDirectory, fileOrDirectoryPath, configFileName, @@ -1092,7 +1115,7 @@ export function createWatchProgram(host: WatchCompiler currentDirectory, useCaseSensitiveFileNames, writeLog, - toPath, + toPath: toPathWorker, })) return; // Reload is pending, do the reload @@ -1119,13 +1142,13 @@ export function createWatchProgram(host: WatchCompiler (_fileName, eventKind) => { updateCachedSystemWithFile(extendedConfigFileName, extendedConfigFilePath, eventKind); // Update extended config cache - if (extendedConfigCache) cleanExtendedConfigCache(extendedConfigCache, extendedConfigFilePath, toPath); + if (extendedConfigCache) cleanExtendedConfigCache(extendedConfigCache, extendedConfigFilePath, toPathWorker); // Update projects const projects = sharedExtendedConfigFileWatchers.get(extendedConfigFilePath)?.projects; // If there are no referenced projects this extended config file watcher depend on ignore if (!projects?.size) return; projects.forEach(projectPath => { - if (configFileName && toPath(configFileName) === projectPath) { + if (configFileName && toPathWorker(configFileName) === projectPath) { // If this is the config file of the project, reload completely reloadLevel = ConfigFileProgramReloadLevel.Full; } @@ -1142,7 +1165,7 @@ export function createWatchProgram(host: WatchCompiler watchOptions, watchType ), - toPath, + toPathWorker, ); } @@ -1169,7 +1192,7 @@ export function createWatchProgram(host: WatchCompiler (directory, flags) => watchDirectory( directory, fileOrDirectory => { - const fileOrDirectoryPath = toPath(fileOrDirectory); + const fileOrDirectoryPath = toPathWorker(fileOrDirectory); // Since the file existence changed, update the sourceFiles cache if (cachedDirectoryStructureHost) { cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); @@ -1179,7 +1202,7 @@ export function createWatchProgram(host: WatchCompiler const config = parsedConfigs?.get(configPath); if (!config?.parsedCommandLine) return; if (isIgnoredFileFromWildCardWatching({ - watchedDirPath: toPath(directory), + watchedDirPath: toPathWorker(directory), fileOrDirectory, fileOrDirectoryPath, configFileName, @@ -1188,7 +1211,7 @@ export function createWatchProgram(host: WatchCompiler currentDirectory, useCaseSensitiveFileNames, writeLog, - toPath, + toPath: toPathWorker, })) return; // Reload is pending, do the reload diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index 1c7eb04b1193f..7c70ac998ee3d 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -1,59 +1,78 @@ +import { BuilderProgram } from "./builderPublic"; +import { + ExtendedConfigCacheEntry, + isExcludedFile, + matchesExclude, +} from "./commandLineParser"; import { arrayToMap, binarySearch, - BuilderProgram, - closeFileWatcher, compareStringsCaseSensitive, - CompilerOptions, createGetCanonicalFileName, - Debug, - DirectoryWatcherCallback, emptyArray, - emptyFileSystemEntries, - ensureTrailingDirectorySeparator, - ExtendedConfigCacheEntry, - Extension, - FileExtensionInfo, - fileExtensionIsOneOf, - FileSystemEntries, - FileWatcher, - FileWatcherCallback, - FileWatcherEventKind, find, - getBaseFileName, - getDirectoryPath, - getNormalizedAbsolutePath, - hasExtension, + flatten, identity, insertSorted, isArray, - isDeclarationFileName, - isExcludedFile, - isSupportedSourceFileName, map, - matchesExclude, - matchFiles, - mutateMap, noop, - normalizePath, - outFile, - Path, - PollingInterval, - Program, - removeFileExtension, - removeIgnoredPath, - returnNoopFileWatcher, returnTrue, - setSysLog, +} from "./core"; +import { SortedArray, SortedReadonlyArray, +} from "./corePublic"; +import * as Debug from "./debug"; +import { + getSupportedExtensions, + getSupportedExtensionsWithJsonIfResolveJsonModule, + removeFileExtension, supportedJSExtensionsFlat, - timestamp, - toPath as ts_toPath, +} from "./extension"; +import { + emptyFileSystemEntries, + FileSystemEntries, + matchFiles, +} from "./fileMatcher"; +import { isDeclarationFileName } from "./parser"; +import { + ensureTrailingDirectorySeparator, + fileExtensionIs, + fileExtensionIsOneOf, + getBaseFileName, + getDirectoryPath, + getNormalizedAbsolutePath, + hasExtension, + normalizePath, + toPath, +} from "./path"; +import { timestamp } from "./performanceCore"; +import { removeIgnoredPath } from "./resolutionCache"; +import { + PollingInterval, + setSysLog, +} from "./sys"; +import { + CompilerOptions, + DirectoryWatcherCallback, + Extension, + FileExtensionInfo, + FileWatcher, + FileWatcherCallback, + FileWatcherEventKind, + Path, + Program, WatchDirectoryFlags, WatchFileKind, WatchOptions, -} from "./_namespaces/ts"; +} from "./types"; +import { + closeFileWatcher, + mutateMap, + outFile, +} from "./utilities"; +import { returnNoopFileWatcher } from "./watch"; /** * Partial interface of the System thats needed to support the caching of directory structure @@ -132,8 +151,8 @@ export function createCachedDirectoryStructureHost(host: DirectoryStructureHost, realpath: host.realpath && realpath }; - function toPath(fileName: string) { - return ts_toPath(fileName, currentDirectory, getCanonicalFileName); + function toPathWorker(fileName: string) { + return toPath(fileName, currentDirectory, getCanonicalFileName); } function getCachedFileSystemEntries(rootDirPath: Path) { @@ -159,7 +178,7 @@ export function createCachedDirectoryStructureHost(host: DirectoryStructureHost, } function createCachedFileSystemEntries(rootDir: string, rootDirPath: Path) { - if (!host.realpath || ensureTrailingDirectorySeparator(toPath(host.realpath(rootDir))) === rootDirPath) { + if (!host.realpath || ensureTrailingDirectorySeparator(toPathWorker(host.realpath(rootDir))) === rootDirPath) { const resultFromHost: MutableFileSystemEntries = { files: map(host.readDirectory!(rootDir, /*extensions*/ undefined, /*exclude*/ undefined, /*include*/["*.*"]), getBaseNameOfFileName) || [], directories: host.getDirectories!(rootDir) || [] @@ -208,7 +227,7 @@ export function createCachedDirectoryStructureHost(host: DirectoryStructureHost, } function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void { - const path = toPath(fileName); + const path = toPathWorker(fileName); const result = getCachedFileSystemEntriesForBaseDir(path); if (result) { updateFilesOfFileSystemEntry(result, getBaseNameOfFileName(fileName), /*fileExists*/ true); @@ -217,19 +236,19 @@ export function createCachedDirectoryStructureHost(host: DirectoryStructureHost, } function fileExists(fileName: string): boolean { - const path = toPath(fileName); + const path = toPathWorker(fileName); const result = getCachedFileSystemEntriesForBaseDir(path); return result && hasEntry(result.sortedAndCanonicalizedFiles, getCanonicalFileName(getBaseNameOfFileName(fileName))) || host.fileExists(fileName); } function directoryExists(dirPath: string): boolean { - const path = toPath(dirPath); + const path = toPathWorker(dirPath); return cachedReadDirectoryResult.has(ensureTrailingDirectorySeparator(path)) || host.directoryExists!(dirPath); } function createDirectory(dirPath: string) { - const path = toPath(dirPath); + const path = toPathWorker(dirPath); const result = getCachedFileSystemEntriesForBaseDir(path); if (result) { const baseName = getBaseNameOfFileName(dirPath); @@ -244,7 +263,7 @@ export function createCachedDirectoryStructureHost(host: DirectoryStructureHost, } function getDirectories(rootDir: string): string[] { - const rootDirPath = toPath(rootDir); + const rootDirPath = toPathWorker(rootDir); const result = tryReadDirectory(rootDir, rootDirPath); if (result) { return result.directories.slice(); @@ -253,7 +272,7 @@ export function createCachedDirectoryStructureHost(host: DirectoryStructureHost, } function readDirectory(rootDir: string, extensions?: readonly string[], excludes?: readonly string[], includes?: readonly string[], depth?: number): string[] { - const rootDirPath = toPath(rootDir); + const rootDirPath = toPathWorker(rootDir); const rootResult = tryReadDirectory(rootDir, rootDirPath); let rootSymLinkResult: FileSystemEntries | undefined; if (rootResult !== undefined) { @@ -262,7 +281,7 @@ export function createCachedDirectoryStructureHost(host: DirectoryStructureHost, return host.readDirectory!(rootDir, extensions, excludes, includes, depth); function getFileSystemEntries(dir: string): FileSystemEntries { - const path = toPath(dir); + const path = toPathWorker(dir); if (path === rootDirPath) { return rootResult || getFileSystemEntriesFromHost(dir, path); } @@ -805,3 +824,15 @@ export function getFallbackOptions(options: WatchOptions | undefined): WatchOpti export function closeFileWatcherOf(objWithWatcher: T) { objWithWatcher.watcher.close(); } + +function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]) { + if (!fileName) return false; + + const supportedExtensions = getSupportedExtensions(compilerOptions, extraFileExtensions); + for (const extension of flatten(getSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions, supportedExtensions))) { + if (fileExtensionIs(fileName, extension)) { + return true; + } + } + return false; +} diff --git a/src/deprecatedCompat/5.0/identifierProperties.ts b/src/deprecatedCompat/5.0/identifierProperties.ts index fbbfc5ee55d15..2b90ba57f8470 100644 --- a/src/deprecatedCompat/5.0/identifierProperties.ts +++ b/src/deprecatedCompat/5.0/identifierProperties.ts @@ -1,10 +1,7 @@ -import { - addObjectAllocatorPatcher, - hasProperty, - Identifier, - identifierToKeywordKind, - NodeFlags, -} from "../_namespaces/ts"; +import { hasProperty } from "../../compiler/core"; +import { addObjectAllocatorPatcher } from "../../compiler/objectAllocator"; +import { Identifier, NodeFlags } from "../../compiler/types"; +import { identifierToKeywordKind } from "../../compiler/utilitiesPublic"; import { deprecate } from "../deprecate"; declare module "../../compiler/types" { diff --git a/src/deprecatedCompat/deprecate.ts b/src/deprecatedCompat/deprecate.ts index 661da6980d437..9584a6ac354d4 100644 --- a/src/deprecatedCompat/deprecate.ts +++ b/src/deprecatedCompat/deprecate.ts @@ -1,11 +1,9 @@ -import { - Debug, - DeprecationOptions, - formatStringFromArgs, - noop, - Version, - version, -} from "./_namespaces/ts"; +import { noop } from "../compiler/core"; +import { version } from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { Version } from "../compiler/semver"; +import { formatStringFromArgs } from "../compiler/utilities"; +import { DeprecationOptions } from "./deprecations"; export let enableDeprecationWarnings = true; diff --git a/src/deprecatedCompat/deprecations.ts b/src/deprecatedCompat/deprecations.ts index 8682631b07830..5a6a4ca1dff7d 100644 --- a/src/deprecatedCompat/deprecations.ts +++ b/src/deprecatedCompat/deprecations.ts @@ -1,8 +1,6 @@ -import { - hasProperty, - UnionToIntersection, - Version, -} from "./_namespaces/ts"; +import { hasProperty } from "../compiler/core"; +import { Version } from "../compiler/semver"; +import { UnionToIntersection } from "../compiler/types"; import { deprecate } from "./deprecate"; /** @internal */ @@ -16,7 +14,6 @@ export interface DeprecationOptions { name?: string; } - // The following are deprecations for the public API. Deprecated exports are removed from the compiler itself // and compatible implementations are added here, along with an appropriate deprecation warning using // the `@deprecated` JSDoc tag as well as the `deprecate` API. diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index b6f1d1ddedd7c..2d17395ca6a3c 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -1,94 +1,116 @@ -import * as performance from "../compiler/performance"; import { - arrayFrom, BuilderProgram, - BuildOptions, + EmitAndSemanticDiagnosticsBuilderProgram, +} from "../compiler/builderPublic"; +import { buildOpts, - changeCompilerHostLikeToUseCache, - CharacterCodes, - combinePaths, - CommandLineOption, - compareStringsCaseInsensitive, - CompilerOptions, - contains, convertToOptionsWithAbsolutePaths, convertToTSConfig, - createBuilderStatusReporter, - createCompilerDiagnostic, - createCompilerHostWorker, - createDiagnosticReporter, - createGetCanonicalFileName, - createIncrementalCompilerHost, - CreateProgram, - createProgram, - CreateProgramOptions, - createSolutionBuilder, - createSolutionBuilderHost, - createSolutionBuilderWithWatch, - createSolutionBuilderWithWatchHost, - createWatchCompilerHostOfConfigFile, - createWatchCompilerHostOfFilesAndCompilerOptions, - createWatchProgram, - createWatchStatusReporter as ts_createWatchStatusReporter, - Debug, - Diagnostic, - DiagnosticMessage, DiagnosticReporter, - Diagnostics, - dumpTracingLegend, - EmitAndSemanticDiagnosticsBuilderProgram, - emitFilesAndReportErrorsAndGetExitStatus, - ExitStatus, ExtendedConfigCacheEntry, - Extension, - fileExtensionIs, - fileExtensionIsOneOf, - filter, - findConfigFile, - forEach, - formatMessage, generateTSConfig, - getBuildOrderFromAnyBuildOrder, getCompilerOptionsDiffValue, - getConfigFileParsingDiagnostics, getDiagnosticText, - getErrorSummaryText, - getLineStarts, - getNormalizedAbsolutePath, - isIncrementalCompilation, - isWatchSet, - normalizePath, optionDeclarations, optionsForBuild, optionsForWatch, - padLeft, - padRight, parseBuildCommand, parseCommandLine, - parseConfigFileWithSystem, - ParsedCommandLine, - performIncrementalCompilation as ts_performIncrementalCompilation, - Program, +} from "../compiler/commandLineParser"; +import { + arrayFrom, + compareStringsCaseInsensitive, + contains, + createGetCanonicalFileName, + filter, + forEach, + padLeft, + padRight, reduceLeftIterator, - ReportEmitErrorSummary, - SolutionBuilder, - SolutionBuilderHostBase, sort, - SourceFile, startsWith, - startTracing, stringContains, +} from "../compiler/core"; +import { version } from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { Diagnostics } from "../compiler/diagnosticInformationMap.generated"; +import { supportedJSExtensionsFlat, supportedTSExtensionsFlat, - sys, - System, +} from "../compiler/extension"; +import { + combinePaths, + fileExtensionIs, + fileExtensionIsOneOf, + getNormalizedAbsolutePath, + normalizePath, toPath, +} from "../compiler/path"; +import * as performance from "../compiler/performance"; +import { + changeCompilerHostLikeToUseCache, + createCompilerHostWorker, + createProgram, + findConfigFile, + getConfigFileParsingDiagnostics, +} from "../compiler/program"; +import { getLineStarts } from "../compiler/scanner"; +import { sys } from "../compiler/sys"; +import { + dumpTracingLegend, + startTracing, tracing, - validateLocaleAndSetLanguage, - version, - WatchCompilerHost, +} from "../compiler/tracing"; +import { + BuildOptions, + createBuilderStatusReporter, + createSolutionBuilder, + createSolutionBuilderHost, + createSolutionBuilderWithWatch, + createSolutionBuilderWithWatchHost, + getBuildOrderFromAnyBuildOrder, + ReportEmitErrorSummary, + SolutionBuilder, + SolutionBuilderHostBase, +} from "../compiler/tsbuildPublic"; +import { + CharacterCodes, + CommandLineOption, + CompilerOptions, + CreateProgramOptions, + Diagnostic, + DiagnosticMessage, + ExitStatus, + Extension, + ParsedCommandLine, + Program, + SourceFile, + System, WatchOptions, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + createCompilerDiagnostic, + formatMessage, + isIncrementalCompilation, + isWatchSet, +} from "../compiler/utilities"; +import { validateLocaleAndSetLanguage } from "../compiler/utilitiesPublic"; +import { + createDiagnosticReporter, + createWatchCompilerHostOfConfigFile, + createWatchCompilerHostOfFilesAndCompilerOptions, + createWatchStatusReporter, + emitFilesAndReportErrorsAndGetExitStatus, + getErrorSummaryText, + parseConfigFileWithSystem, + performIncrementalCompilation, +} from "../compiler/watch"; +import { + createIncrementalCompilerHost, + CreateProgram, + createWatchProgram, + WatchCompilerHost, +} from "../compiler/watchPublic"; interface Statistic { name: string; @@ -674,7 +696,7 @@ function executeCommandLineWorker( ); } else if (isIncrementalCompilation(configParseResult.options)) { - performIncrementalCompilation( + performIncrementalCompilationWorker( sys, cb, reportDiagnostic, @@ -713,7 +735,7 @@ function executeCommandLineWorker( ); } else if (isIncrementalCompilation(commandLineOptions)) { - performIncrementalCompilation( + performIncrementalCompilationWorker( sys, cb, reportDiagnostic, @@ -839,7 +861,7 @@ function performBuild( /*createProgram*/ undefined, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty(sys, buildOptions)), - createWatchStatusReporter(sys, buildOptions) + createWatchStatusReporterWorker(sys, buildOptions) ); const solutionPerformance = enableSolutionPerformance(sys, buildOptions); updateSolutionBuilderHost(sys, cb, buildHost, solutionPerformance); @@ -915,7 +937,7 @@ function performCompilation( return sys.exit(exitStatus); } -function performIncrementalCompilation( +function performIncrementalCompilationWorker( sys: System, cb: ExecuteCommandLineCallbacks, reportDiagnostic: DiagnosticReporter, @@ -924,7 +946,7 @@ function performIncrementalCompilation( const { options, fileNames, projectReferences } = config; enableStatisticsAndTracing(sys, options, /*isBuildMode*/ false); const host = createIncrementalCompilerHost(options, sys); - const exitStatus = ts_performIncrementalCompilation({ + const exitStatus = performIncrementalCompilation({ host, system: sys, rootNames: fileNames, @@ -984,8 +1006,8 @@ function updateWatchCompilationHost( }; } -function createWatchStatusReporter(sys: System, options: CompilerOptions | BuildOptions) { - return ts_createWatchStatusReporter(sys, shouldBePretty(sys, options)); +function createWatchStatusReporterWorker(sys: System, options: CompilerOptions | BuildOptions) { + return createWatchStatusReporter(sys, shouldBePretty(sys, options)); } function createWatchOfConfigFile( @@ -1003,7 +1025,7 @@ function createWatchOfConfigFile( watchOptionsToExtend, system, reportDiagnostic, - reportWatchStatus: createWatchStatusReporter(system, configParseResult.options) + reportWatchStatus: createWatchStatusReporterWorker(system, configParseResult.options) }); updateWatchCompilationHost(system, cb, watchCompilerHost); watchCompilerHost.configFileParsingResult = configParseResult; @@ -1025,7 +1047,7 @@ function createWatchOfFilesAndCompilerOptions( watchOptions, system, reportDiagnostic, - reportWatchStatus: createWatchStatusReporter(system, options) + reportWatchStatus: createWatchStatusReporterWorker(system, options) }); updateWatchCompilationHost(system, cb, watchCompilerHost); return createWatchProgram(watchCompilerHost); @@ -1039,7 +1061,7 @@ interface SolutionPerformance { function enableSolutionPerformance(system: System, options: BuildOptions) { if (system === sys && options.extendedDiagnostics) { - performance.enable(); + performance.enable(sys); return createSolutionPerfomrance(); } } @@ -1097,7 +1119,7 @@ function reportSolutionBuilderTimes( if (isSolutionMarkOrMeasure(name)) statistics.push({ name: `${getNameFromSolutionBuilderMarkOrMeasure(name)} time`, value: duration, type: StatisticType.time }); }); performance.disable(); - performance.enable(); + performance.enable(sys); solutionPerformance.clear(); reportAllStatistics(sys, statistics); diff --git a/src/harness/fakesHosts.ts b/src/harness/fakesHosts.ts index 25706a1dde66a..0138c65058229 100644 --- a/src/harness/fakesHosts.ts +++ b/src/harness/fakesHosts.ts @@ -1,3 +1,5 @@ +import { FileSystemEntries, matchFiles } from "../compiler/fileMatcher"; +import { getNewLineCharacter } from "../compiler/utilities"; import * as collections from "./_namespaces/collections"; import * as documents from "./_namespaces/documents"; import * as Harness from "./_namespaces/Harness"; @@ -104,10 +106,10 @@ export class System implements ts.System { } public readDirectory(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[] { - return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, path => this.getAccessibleFileSystemEntries(path), path => this.realpath(path)); + return matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, path => this.getAccessibleFileSystemEntries(path), path => this.realpath(path)); } - public getAccessibleFileSystemEntries(path: string): ts.FileSystemEntries { + public getAccessibleFileSystemEntries(path: string): FileSystemEntries { const files: string[] = []; const directories: string[] = []; try { @@ -243,7 +245,7 @@ export class CompilerHost implements ts.CompilerHost { if (sys instanceof vfs.FileSystem) sys = new System(sys); this.sys = sys; this.defaultLibLocation = sys.vfs.meta.get("defaultLibLocation") || ""; - this._newLine = ts.getNewLineCharacter(options); + this._newLine = getNewLineCharacter(options); this._sourceFiles = new collections.SortedMap({ comparer: sys.vfs.stringComparer, sort: "insertion" }); this._setParentNodes = setParentNodes; this._outputsMap = new collections.SortedMap(this.vfs.stringComparer); diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 2b6b93dffcdeb..99a3f9fa8fd98 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -1,3 +1,5 @@ +import { regExpEscape } from "../compiler/fileMatcher"; +import { applyChanges } from "../services/textChanges"; import * as fakes from "./_namespaces/fakes"; import * as FourSlashInterface from "./_namespaces/FourSlashInterface"; import * as Harness from "./_namespaces/Harness"; @@ -3285,7 +3287,7 @@ export class TestState { assert(changes.length === 1, "Affected 0 or more than 1 file, must use 'newFileContent' instead of 'newRangeContent'"); const change = ts.first(changes); assert(change.fileName = this.activeFile.fileName); - const newText = ts.textChanges.applyChanges(this.getFileContent(this.activeFile.fileName), change.textChanges); + const newText = applyChanges(this.getFileContent(this.activeFile.fileName), change.textChanges); const newRange = updateTextRangeForTextChanges(this.getOnlyRange(), change.textChanges); const actualText = newText.slice(newRange.pos, newRange.end); this.verifyTextMatches(actualText, /*includeWhitespace*/ true, newRangeContent); @@ -3299,7 +3301,7 @@ export class TestState { ts.Debug.fail(`Did not expect a change in ${change.fileName}`); } const oldText = this.tryGetFileContent(change.fileName); - const newContent = change.isNewFile ? ts.first(change.textChanges).newText : ts.textChanges.applyChanges(oldText!, change.textChanges); + const newContent = change.isNewFile ? ts.first(change.textChanges).newText : applyChanges(oldText!, change.textChanges); this.verifyTextMatches(newContent, /*includeWhitespace*/ true, expectedNewContent); } for (const newFileName in newFileContent) { @@ -3932,7 +3934,7 @@ export class TestState { const fileContent = this.tryGetFileContent(fileName); if (fileContent !== undefined) { - const actualNewContent = ts.textChanges.applyChanges(fileContent, textChanges); + const actualNewContent = applyChanges(fileContent, textChanges); assert.equal(actualNewContent, newContent, `new content for ${fileName}`); } else { @@ -4855,7 +4857,7 @@ function displayExpectedAndActualString(expected: string, actual: string, quoted } function templateToRegExp(template: string) { - return new RegExp(`^${ts.regExpEscape(template).replace(/\\\{\d+\\\}/g, ".*?")}$`); + return new RegExp(`^${regExpEscape(template).replace(/\\\{\d+\\\}/g, ".*?")}$`); } function rangesOfDiffBetweenTwoStrings(source: string, target: string) { diff --git a/src/harness/harnessIO.ts b/src/harness/harnessIO.ts index f49eda407ffec..77684cc6b4999 100644 --- a/src/harness/harnessIO.ts +++ b/src/harness/harnessIO.ts @@ -1,3 +1,4 @@ +import { FileSystemEntries } from "../compiler/fileMatcher"; import * as compiler from "./_namespaces/compiler"; import * as documents from "./_namespaces/documents"; import * as fakes from "./_namespaces/fakes"; @@ -33,7 +34,7 @@ export interface IO { getWorkspaceRoot(): string; exit(exitCode?: number): void; readDirectory(path: string, extension?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): readonly string[]; - getAccessibleFileSystemEntries(dirname: string): ts.FileSystemEntries; + getAccessibleFileSystemEntries(dirname: string): FileSystemEntries; tryEnableSourceMapsForHost?(): void; getEnvironmentVariable?(name: string): string; getMemoryUsage?(): number | undefined; @@ -106,7 +107,7 @@ function createNodeIO(): IO { return filesInFolder(path); } - function getAccessibleFileSystemEntries(dirname: string): ts.FileSystemEntries { + function getAccessibleFileSystemEntries(dirname: string): FileSystemEntries { try { const entries: string[] = fs.readdirSync(dirname || ".").sort(ts.sys.useCaseSensitiveFileNames ? ts.compareStringsCaseSensitive : ts.compareStringsCaseInsensitive); const files: string[] = []; diff --git a/src/harness/harnessUtils.ts b/src/harness/harnessUtils.ts index beee8048030a8..a14730652a785 100644 --- a/src/harness/harnessUtils.ts +++ b/src/harness/harnessUtils.ts @@ -1,3 +1,4 @@ +import { containsParseError } from "../compiler/parserUtilities"; import * as Harness from "./_namespaces/Harness"; import * as ts from "./_namespaces/ts"; @@ -180,7 +181,7 @@ export function sourceFileToJSON(file: ts.Node): string { function serializeNode(n: ts.Node): any { const o: any = { kind: getKindName(n.kind) }; - if (ts.containsParseError(n)) { + if (containsParseError(n)) { o.containsParseError = true; } @@ -289,7 +290,7 @@ export function assertStructuralEquals(node1: ts.Node, node2: ts.Node) { // call this on both nodes to ensure all propagated flags have been set (and thus can be // compared). - assert.equal(ts.containsParseError(node1), ts.containsParseError(node2)); + assert.equal(containsParseError(node1), containsParseError(node2)); assert.equal(node1.flags & ~ts.NodeFlags.ReachabilityAndEmitFlags, node2.flags & ~ts.NodeFlags.ReachabilityAndEmitFlags, "node1.flags !== node2.flags"); ts.forEachChild(node1, diff --git a/src/harness/util.ts b/src/harness/util.ts index 2dac1835f1293..8e9e5f84d32ab 100644 --- a/src/harness/util.ts +++ b/src/harness/util.ts @@ -1,3 +1,4 @@ +import { regExpEscape } from "../compiler/fileMatcher"; import * as ts from "./_namespaces/ts"; /** @@ -11,7 +12,7 @@ export function removeTestPathPrefixes(text: string, retainTrailingDirectorySepa function createDiagnosticMessageReplacer string[]>(diagnosticMessage: ts.DiagnosticMessage, replacer: R) { const messageParts = diagnosticMessage.message.split(/{\d+}/g); - const regExp = new RegExp(`^(?:${messageParts.map(ts.regExpEscape).join("(.*?)")})$`); + const regExp = new RegExp(`^(?:${messageParts.map(regExpEscape).join("(.*?)")})$`); type Args = R extends (messageArgs: string[], ...args: infer A) => string[] ? A : []; return (text: string, ...args: Args) => text.replace(regExp, (_, ...fixedArgs) => ts.formatStringFromArgs(diagnosticMessage.message, replacer(fixedArgs, ...args))); } diff --git a/src/harness/vfsUtil.ts b/src/harness/vfsUtil.ts index 6968b34914d81..a6cb0e6b6f6bb 100644 --- a/src/harness/vfsUtil.ts +++ b/src/harness/vfsUtil.ts @@ -1,3 +1,4 @@ +import { FileSystemEntries } from "../compiler/fileMatcher"; import * as collections from "./_namespaces/collections"; import * as documents from "./_namespaces/documents"; import * as Harness from "./_namespaces/Harness"; @@ -1191,7 +1192,7 @@ export interface FileSystemResolver { export interface FileSystemResolverHost { useCaseSensitiveFileNames(): boolean; - getAccessibleFileSystemEntries(path: string): ts.FileSystemEntries; + getAccessibleFileSystemEntries(path: string): FileSystemEntries; directoryExists(path: string): boolean; fileExists(path: string): boolean; getFileSize(path: string): number; diff --git a/src/jsTyping/jsTyping.ts b/src/jsTyping/jsTyping.ts index 1983107b35548..dcfc5ac3cd51a 100644 --- a/src/jsTyping/jsTyping.ts +++ b/src/jsTyping/jsTyping.ts @@ -1,35 +1,43 @@ +import { readConfigFile } from "../compiler/commandLineParser"; import { - CharacterCodes, - combinePaths, compareStringsCaseSensitive, - CompilerOptions, - Debug, deduplicate, equateStringsCaseSensitive, - Extension, - fileExtensionIs, flatMap, forEach, - getBaseFileName, - getDirectoryPath, - getNormalizedAbsolutePath, getOwnKeys, - getPathComponents, getProperty, - hasJSFileExtension, mapDefined, - MapLike, - normalizePath, - Path, - readConfigFile, - removeFileExtension, removeMinAndVersionNumbers, some, toFileNameLowerCase, - TypeAcquisition, - Version, +} from "../compiler/core"; +import { + MapLike, versionMajorMinor, -} from "./_namespaces/ts"; +} from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { + hasJSFileExtension, + removeFileExtension, +} from "../compiler/extension"; +import { + combinePaths, + fileExtensionIs, + getBaseFileName, + getDirectoryPath, + getNormalizedAbsolutePath, + getPathComponents, + normalizePath, +} from "../compiler/path"; +import { Version } from "../compiler/semver"; +import { + CharacterCodes, + CompilerOptions, + Extension, + Path, + TypeAcquisition, +} from "../compiler/types"; export interface TypingResolutionHost { directoryExists(path: string): boolean; diff --git a/src/jsTyping/shared.ts b/src/jsTyping/shared.ts index 5ae0e09354c75..4e8023b11d23e 100644 --- a/src/jsTyping/shared.ts +++ b/src/jsTyping/shared.ts @@ -1,7 +1,5 @@ -import { - padLeft, - sys, -} from "./_namespaces/ts"; +import { padLeft } from "../compiler/core"; +import { sys } from "../compiler/sys"; export type ActionSet = "action::set"; export type ActionInvalidate = "action::invalidate"; diff --git a/src/jsTyping/types.ts b/src/jsTyping/types.ts index a9991a2f086c7..14005edac2b00 100644 --- a/src/jsTyping/types.ts +++ b/src/jsTyping/types.ts @@ -1,11 +1,13 @@ import { - CompilerOptions, - JsTyping, MapLike, - Path, SortedReadonlyArray, +} from "../compiler/corePublic"; +import { + CompilerOptions, + Path, TypeAcquisition, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import * as JsTyping from "./jsTyping"; import { ActionInvalidate, ActionPackageInstalled, @@ -15,7 +17,7 @@ import { EventEndInstallTypes, EventInitializationFailed, EventTypesRegistry, -} from "./_namespaces/ts.server"; +} from "./shared"; export interface TypingInstallerResponse { readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | ActionPackageInstalled | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed | ActionWatchTypingLocations; diff --git a/src/loggedIO/loggedIO.ts b/src/loggedIO/loggedIO.ts index 93c596b7f5e33..b435629733899 100644 --- a/src/loggedIO/loggedIO.ts +++ b/src/loggedIO/loggedIO.ts @@ -1,5 +1,27 @@ -import * as Harness from "./_namespaces/Harness"; -import * as ts from "./_namespaces/ts"; +import { parseCommandLine } from "../compiler/commandLineParser"; +import { + createGetCanonicalFileName, + flatMap, + forEach, + hasProperty, + startsWith, +} from "../compiler/core"; +import { + combinePaths, + isRootedDiskPath, + normalizePath, + normalizeSlashes, + toPath, +} from "../compiler/path"; +import { + AnyFunction, + CompilerOptions, + System, +} from "../compiler/types"; +import { + IO, + isDefaultLibraryFile, +} from "../harness/harnessIO"; interface FileInformation { contents?: string; @@ -96,7 +118,7 @@ interface Memoized { function memoize(func: (s: string) => T): Memoized { let lookup: { [s: string]: T } = {}; const run: Memoized = ((s: string) => { - if (ts.hasProperty(lookup, s)) return lookup[s]; + if (hasProperty(lookup, s)) return lookup[s]; return lookup[s] = func(s); }) as Memoized; run.reset = () => { @@ -106,9 +128,8 @@ function memoize(func: (s: string) => T): Memoized { return run; } -export interface PlaybackIO extends Harness.IO, PlaybackControl { } - -export interface PlaybackSystem extends ts.System, PlaybackControl { } +export interface PlaybackIO extends IO, PlaybackControl { } +export interface PlaybackSystem extends System, PlaybackControl { } function createEmptyLog(): IoLog { return { @@ -130,16 +151,16 @@ function createEmptyLog(): IoLog { }; } -export function newStyleLogIntoOldStyleLog(log: IoLog, host: ts.System | Harness.IO, baseName: string) { +export function newStyleLogIntoOldStyleLog(log: IoLog, host: System | IO, baseName: string) { for (const file of log.filesAppended) { if (file.contentsPath) { - file.contents = host.readFile(ts.combinePaths(baseName, file.contentsPath)); + file.contents = host.readFile(combinePaths(baseName, file.contentsPath)); delete file.contentsPath; } } for (const file of log.filesWritten) { if (file.contentsPath) { - file.contents = host.readFile(ts.combinePaths(baseName, file.contentsPath)); + file.contents = host.readFile(combinePaths(baseName, file.contentsPath)); delete file.contentsPath; } } @@ -148,28 +169,28 @@ export function newStyleLogIntoOldStyleLog(log: IoLog, host: ts.System | Harness if (result.contentsPath) { // `readFile` strips away a BOM (and actually reinerprets the file contents according to the correct encoding) // - but this has the unfortunate sideeffect of removing the BOM from any outputs based on the file, so we readd it here. - result.contents = (result.bom || "") + host.readFile(ts.combinePaths(baseName, result.contentsPath)); + result.contents = (result.bom || "") + host.readFile(combinePaths(baseName, result.contentsPath)); delete result.contentsPath; } } return log; } -const canonicalizeForHarness = ts.createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ false); // This is done so tests work on windows _and_ linux +const canonicalizeForHarness = createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ false); // This is done so tests work on windows _and_ linux function sanitizeTestFilePath(name: string) { - const path = ts.toPath(ts.normalizeSlashes(name.replace(/[\^<>:"|?*%]/g, "_")).replace(/\.\.\//g, "__dotdot/"), "", canonicalizeForHarness); - if (ts.startsWith(path, "/")) { + const path = toPath(normalizeSlashes(name.replace(/[\^<>:"|?*%]/g, "_")).replace(/\.\.\//g, "__dotdot/"), "", canonicalizeForHarness); + if (startsWith(path, "/")) { return path.substring(1); } return path; } -export function oldStyleLogIntoNewStyleLog(log: IoLog, writeFile: typeof Harness.IO.writeFile, baseTestName: string) { +export function oldStyleLogIntoNewStyleLog(log: IoLog, writeFile: typeof IO.writeFile, baseTestName: string) { if (log.filesAppended) { for (const file of log.filesAppended) { if (file.contents !== undefined) { - file.contentsPath = ts.combinePaths("appended", sanitizeTestFilePath(file.path)); - writeFile(ts.combinePaths(baseTestName, file.contentsPath), file.contents); + file.contentsPath = combinePaths("appended", sanitizeTestFilePath(file.path)); + writeFile(combinePaths(baseTestName, file.contentsPath), file.contents); delete file.contents; } } @@ -177,8 +198,8 @@ export function oldStyleLogIntoNewStyleLog(log: IoLog, writeFile: typeof Harness if (log.filesWritten) { for (const file of log.filesWritten) { if (file.contents !== undefined) { - file.contentsPath = ts.combinePaths("written", sanitizeTestFilePath(file.path)); - writeFile(ts.combinePaths(baseTestName, file.contentsPath), file.contents); + file.contentsPath = combinePaths("written", sanitizeTestFilePath(file.path)); + writeFile(combinePaths(baseTestName, file.contentsPath), file.contents); delete file.contents; } } @@ -188,8 +209,8 @@ export function oldStyleLogIntoNewStyleLog(log: IoLog, writeFile: typeof Harness const result = file.result!; // TODO: GH#18217 const { contents } = result; if (contents !== undefined) { - result.contentsPath = ts.combinePaths("read", sanitizeTestFilePath(file.path)); - writeFile(ts.combinePaths(baseTestName, result.contentsPath), contents); + result.contentsPath = combinePaths("read", sanitizeTestFilePath(file.path)); + writeFile(combinePaths(baseTestName, result.contentsPath), contents); const len = contents.length; if (len >= 2 && contents.charCodeAt(0) === 0xfeff) { result.bom = "\ufeff"; @@ -207,8 +228,8 @@ export function oldStyleLogIntoNewStyleLog(log: IoLog, writeFile: typeof Harness return log; } -export function initWrapper(...[wrapper, underlying]: [PlaybackSystem, ts.System] | [PlaybackIO, Harness.IO]): void { - ts.forEach(Object.keys(underlying), prop => { +export function initWrapper(...[wrapper, underlying]: [PlaybackSystem, System] | [PlaybackIO, IO]): void { + forEach(Object.keys(underlying), prop => { (wrapper as any)[prop] = (underlying as any)[prop]; }); @@ -221,7 +242,7 @@ export function initWrapper(...[wrapper, underlying]: [PlaybackSystem, ts.System replayLog.filesRead = replayLog.filesRead.filter(f => f.result!.contents !== undefined); replayFilesRead = new Map(); for (const file of replayLog.filesRead) { - replayFilesRead.set(ts.normalizeSlashes(file.path).toLowerCase(), file); + replayFilesRead.set(normalizeSlashes(file.path).toLowerCase(), file); } }; @@ -246,18 +267,18 @@ export function initWrapper(...[wrapper, underlying]: [PlaybackSystem, ts.System if (recordLog !== undefined) { let i = 0; const getBase = () => recordLogFileNameBase + i; - while (underlying.fileExists(ts.combinePaths(getBase(), "test.json"))) i++; + while (underlying.fileExists(combinePaths(getBase(), "test.json"))) i++; const newLog = oldStyleLogIntoNewStyleLog(recordLog, (path, str) => underlying.writeFile(path, str), getBase()); - underlying.writeFile(ts.combinePaths(getBase(), "test.json"), JSON.stringify(newLog, null, 4)); // eslint-disable-line no-null/no-null + underlying.writeFile(combinePaths(getBase(), "test.json"), JSON.stringify(newLog, null, 4)); // eslint-disable-line no-null/no-null const syntheticTsconfig = generateTsconfig(newLog); if (syntheticTsconfig) { - underlying.writeFile(ts.combinePaths(getBase(), "tsconfig.json"), JSON.stringify(syntheticTsconfig, null, 4)); // eslint-disable-line no-null/no-null + underlying.writeFile(combinePaths(getBase(), "tsconfig.json"), JSON.stringify(syntheticTsconfig, null, 4)); // eslint-disable-line no-null/no-null } recordLog = undefined; } }; - function generateTsconfig(newLog: IoLog): undefined | { compilerOptions: ts.CompilerOptions, files: string[] } { + function generateTsconfig(newLog: IoLog): undefined | { compilerOptions: CompilerOptions, files: string[] } { if (newLog.filesRead.some(file => /tsconfig.+json$/.test(file.path))) { return; } @@ -265,12 +286,12 @@ export function initWrapper(...[wrapper, underlying]: [PlaybackSystem, ts.System for (const file of newLog.filesRead) { const result = file.result!; if (result.contentsPath && - Harness.isDefaultLibraryFile(result.contentsPath) && + isDefaultLibraryFile(result.contentsPath) && /\.[tj]s$/.test(result.contentsPath)) { files.push(result.contentsPath); } } - return { compilerOptions: ts.parseCommandLine(newLog.arguments).options, files }; + return { compilerOptions: parseCommandLine(newLog.arguments).options, files }; } wrapper.fileExists = recordReplay(wrapper.fileExists, underlying)( @@ -312,7 +333,7 @@ export function initWrapper(...[wrapper, underlying]: [PlaybackSystem, ts.System wrapper.resolvePath = recordReplay(wrapper.resolvePath, underlying)( path => callAndRecord(underlying.resolvePath(path), recordLog!.pathsResolved, { path }), - memoize(path => findResultByFields(replayLog!.pathsResolved, { path }, !ts.isRootedDiskPath(ts.normalizeSlashes(path)) && replayLog!.currentDirectory ? replayLog!.currentDirectory + "/" + path : ts.normalizeSlashes(path)))); + memoize(path => findResultByFields(replayLog!.pathsResolved, { path }, !isRootedDiskPath(normalizeSlashes(path)) && replayLog!.currentDirectory ? replayLog!.currentDirectory + "/" + path : normalizeSlashes(path)))); wrapper.readFile = recordReplay(wrapper.readFile, underlying)( (path: string) => { @@ -325,7 +346,7 @@ export function initWrapper(...[wrapper, underlying]: [PlaybackSystem, ts.System wrapper.readDirectory = recordReplay(wrapper.readDirectory, underlying)( (path, extensions, exclude, include, depth) => { - const result = (underlying as ts.System).readDirectory(path, extensions, exclude, include, depth); + const result = (underlying as System).readDirectory(path, extensions, exclude, include, depth); recordLog!.directoriesRead.push({ path, extensions, exclude, include, depth, result }); return result; }, @@ -334,9 +355,9 @@ export function initWrapper(...[wrapper, underlying]: [PlaybackSystem, ts.System // if each of the directoriesRead has matched path with the given path (directory with same path but different extension will considered // different entry). // TODO (yuisu): We can certainly remove these once we recapture the RWC using new API - const normalizedPath = ts.normalizePath(path).toLowerCase(); - return ts.flatMap(replayLog!.directoriesRead, directory => { - if (ts.normalizeSlashes(directory.path).toLowerCase() === normalizedPath) { + const normalizedPath = normalizePath(path).toLowerCase(); + return flatMap(replayLog!.directoriesRead, directory => { + if (normalizeSlashes(directory.path).toLowerCase() === normalizedPath) { return directory.result; } }); @@ -361,7 +382,7 @@ export function initWrapper(...[wrapper, underlying]: [PlaybackSystem, ts.System }; } -function recordReplay(original: T, underlying: any) { +function recordReplay(original: T, underlying: any) { function createWrapper(record: T, replay: T): T { // eslint-disable-next-line local/only-arrow-functions return (function () { @@ -404,7 +425,7 @@ function findResultByFields(logArray: { result?: T }[], expectedFields: {}, d } function findFileByPath(expectedPath: string, throwFileNotFoundError: boolean): FileInformation | undefined { - const normalizedName = ts.normalizePath(expectedPath).toLowerCase(); + const normalizedName = normalizePath(expectedPath).toLowerCase(); // Try to find the result through normal fileName const result = replayFilesRead!.get(normalizedName); if (result) { @@ -424,7 +445,7 @@ function noOpReplay(_name: string) { // console.log("Swallowed write operation during replay: " + name); } -export function wrapIO(underlying: Harness.IO): PlaybackIO { +export function wrapIO(underlying: IO): PlaybackIO { const wrapper: PlaybackIO = {} as any; initWrapper(wrapper, underlying); @@ -441,7 +462,7 @@ export function wrapIO(underlying: Harness.IO): PlaybackIO { } } -export function wrapSystem(underlying: ts.System): PlaybackSystem { +export function wrapSystem(underlying: System): PlaybackSystem { const wrapper: PlaybackSystem = {} as any; initWrapper(wrapper, underlying); return wrapper; diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 5335620429851..cb25aedc0daef 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1,159 +1,216 @@ import { - addToSeen, - arrayFrom, - arrayToMap, - AssertionLevel, - CachedDirectoryStructureHost, canJsonReportNoInputFiles, - canWatchDirectoryOrFile, - cleanExtendedConfigCache, - clearMap, - clearSharedExtendedConfigFileWatcher, - closeFileWatcherOf, - combinePaths, - CommandLineOption, - CompilerOptions, - CompletionInfo, - ConfigFileProgramReloadLevel, - contains, - containsPath, convertCompilerOptionsForTelemetry, convertJsonOption, - createCachedDirectoryStructureHost, - createDocumentRegistryInternal, + ExtendedConfigCacheEntry, + getFileNamesFromConfigSpecs, + optionDeclarations, + optionsForWatch, + parseJsonSourceFileConfigFileContent, + tryReadFile, + typeAcquisitionDeclarations, +} from "../compiler/commandLineParser"; +import { + arrayFrom, + arrayToMap, + contains, createGetCanonicalFileName, createMultiMap, - Debug, - Diagnostic, - directorySeparator, - DirectoryStructureHost, - DocumentPosition, - DocumentPositionMapper, - DocumentRegistry, - DocumentRegistryBucketKeyWithMode, - emptyOptions, - ensureTrailingDirectorySeparator, - ExtendedConfigCacheEntry, - FileExtensionInfo, - fileExtensionIs, - FileWatcher, - FileWatcherEventKind, find, flatMap, forEach, - forEachAncestorDirectory, - forEachEntry, - forEachKey, - forEachResolvedProjectReference, - FormatCodeSettings, - getAnyExtensionFromPath, - getBaseFileName, - getDefaultFormatCodeSettings, - getDirectoryPath, - getDocumentPositionMapper, - getFileNamesFromConfigSpecs, - getFileWatcherEventKind, - getNormalizedAbsolutePath, - getPathComponents, - getSnapshotText, - getWatchFactory, - hasExtension, hasProperty, - hasTSFileExtension, - HostCancellationToken, identity, - IncompleteCompletionsCache, - IndentStyle, isArray, - isIgnoredFileFromWildCardWatching, - isInsideNodeModules, - isJsonEqual, - isNodeModulesDirectory, - isRootedDiskPath, isString, - LanguageServiceMode, length, map, mapDefinedEntries, mapDefinedIterator, - missingFileModifiedTime, MultiMap, noop, + removeMinAndVersionNumbers, + returnTrue, + some, + startsWith, + toFileNameLowerCase, + tryAddToSet, + unorderedRemoveItem, +} from "../compiler/core"; +import { + ReadonlyCollection, + version, +} from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { + hasTSFileExtension, + removeFileExtension, +} from "../compiler/extension"; +import { parsePackageName } from "../compiler/moduleNameResolver"; +import { parseJsonText } from "../compiler/parser"; +import { + combinePaths, + containsPath, + directorySeparator, + ensureTrailingDirectorySeparator, + fileExtensionIs, + forEachAncestorDirectory, + getAnyExtensionFromPath, + getBaseFileName, + getDirectoryPath, + getNormalizedAbsolutePath, + getPathComponents, + hasExtension, + isNodeModulesDirectory, + isRootedDiskPath, normalizePath, normalizeSlashes, - optionDeclarations, - optionsForWatch, - PackageJsonAutoImportPreference, + toPath, +} from "../compiler/path"; +import { + forEachResolvedProjectReference, + resolveProjectReferencePath, +} from "../compiler/program"; +import { + canWatchDirectoryOrFile, + removeIgnoredPath, +} from "../compiler/resolutionCache"; +import { + getFileWatcherEventKind, + missingFileModifiedTime, + PollingInterval, +} from "../compiler/sys"; +import { tracing } from "../compiler/tracing"; +import { + CommandLineOption, + CompilerOptions, + Diagnostic, + DocumentPosition, + DocumentPositionMapper, + FileExtensionInfo, + FileWatcher, + FileWatcherEventKind, ParsedCommandLine, - parseJsonSourceFileConfigFileContent, - parseJsonText, - parsePackageName, Path, - PerformanceEvent, PluginImport, - PollingInterval, - ProjectPackageJsonInfo, ProjectReference, - ReadMapFile, - ReadonlyCollection, - removeFileExtension, - removeIgnoredPath, - removeMinAndVersionNumbers, ResolvedProjectReference, - resolveProjectReferencePath, - returnNoopFileWatcher, - returnTrue, ScriptKind, - SharedExtendedConfigFileWatcher, - some, SourceFile, SourceFileLike, - startsWith, Ternary, - TextChange, - toFileNameLowerCase, - toPath, - tracing, - tryAddToSet, - tryReadFile, TsConfigSourceFile, TypeAcquisition, - typeAcquisitionDeclarations, - unorderedRemoveItem, - updateSharedExtendedConfigFileWatcher, - updateWatchingWildcardDirectories, UserPreferences, - version, WatchDirectoryFlags, - WatchFactory, - WatchLogLevel, WatchOptions, +} from "../compiler/types"; +import { + addToSeen, + clearMap, + forEachEntry, + forEachKey, + isJsonEqual, +} from "../compiler/utilities"; +import { + returnNoopFileWatcher, WatchType, +} from "../compiler/watch"; +import { + CachedDirectoryStructureHost, + cleanExtendedConfigCache, + clearSharedExtendedConfigFileWatcher, + closeFileWatcherOf, + ConfigFileProgramReloadLevel, + createCachedDirectoryStructureHost, + DirectoryStructureHost, + getWatchFactory, + isIgnoredFileFromWildCardWatching, + SharedExtendedConfigFileWatcher, + updateSharedExtendedConfigFileWatcher, + updateWatchingWildcardDirectories, + WatchFactory, + WatchLogLevel, WildcardDirectoryWatcher, -} from "./_namespaces/ts"; +} from "../compiler/watchUtilities"; import { ActionInvalidate, ActionSet, - asNormalizedPath, +} from "../jsTyping/shared"; +import { + BeginInstallTypes, + EndInstallTypes, + InvalidateCachedTypings, + PackageInstalledResponse, + SetTypings, + WatchTypingLocations, +} from "../jsTyping/types"; +import { + createDocumentRegistryInternal, + DocumentRegistry, + DocumentRegistryBucketKeyWithMode, +} from "../services/documentRegistry"; +import { + getDocumentPositionMapper, + ReadMapFile, +} from "../services/sourcemaps"; +import { + CompletionInfo, + emptyOptions, + FormatCodeSettings, + getDefaultFormatCodeSettings, + HostCancellationToken, + IncompleteCompletionsCache, + IndentStyle, + LanguageServiceMode, + PackageJsonAutoImportPreference, + PerformanceEvent, + ProjectPackageJsonInfo, + TextChange, +} from "../services/types"; +import { + getSnapshotText, + isInsideNodeModules, +} from "../services/utilities"; +import { + createPackageJsonCache, + PackageJsonCache, +} from "./packageJsonCache"; +import { AutoImportProviderProject, BeginEnablePluginResult, - BeginInstallTypes, ConfiguredProject, countEachFileTypes, - createPackageJsonCache, - emptyArray, - EndInstallTypes, - Errors, ExternalProject, - getBaseConfigFileName, hasNoTypeScriptSource, InferredProject, - InvalidateCachedTypings, isConfiguredProject, - isDynamicFileName, isInferredProject, - isInferredProjectName, + Project, + ProjectFilesWithTSDiagnostics, + ProjectKind, +} from "./project"; +import * as protocol from "./protocol"; +import { + isDynamicFileName, + ScriptInfo, +} from "./scriptInfo"; +import { Session } from "./session"; +import { ServerHost } from "./types"; +import { ITypingsInstaller, + nullTypingsInstaller, + TypingsCache, +} from "./typingsCache"; +import { + getBaseConfigFileName, + ThrottledOperations, +} from "./utilities"; +import { + asNormalizedPath, + emptyArray, + Errors, + isInferredProjectName, Logger, LogLevel, makeAutoImportProviderProjectName, @@ -162,23 +219,9 @@ import { Msg, NormalizedPath, normalizedPathToPath, - nullTypingsInstaller, - PackageInstalledResponse, - PackageJsonCache, - Project, - ProjectFilesWithTSDiagnostics, - ProjectKind, ProjectOptions, - ScriptInfo, - ServerHost, - Session, - SetTypings, - ThrottledOperations, toNormalizedPath, - TypingsCache, - WatchTypingLocations, -} from "./_namespaces/ts.server"; -import * as protocol from "./protocol"; +} from "./utilitiesPublic"; export const maxProgramSizeForNonTsFiles = 20 * 1024 * 1024; /** @internal */ @@ -1632,7 +1675,7 @@ export class ProjectService { project.print(/*writeProjectFileNames*/ true, /*writeFileExplaination*/ true, /*writeFileVersionAndText*/ false); project.close(); - if (Debug.shouldAssert(AssertionLevel.Normal)) { + if (Debug.shouldAssert(Debug.AssertionLevel.Normal)) { this.filenameToScriptInfo.forEach(info => Debug.assert( !info.isAttached(project), "Found script Info still attached to project", diff --git a/src/server/moduleSpecifierCache.ts b/src/server/moduleSpecifierCache.ts index 2f2142a5bb0fe..6358ee0e41594 100644 --- a/src/server/moduleSpecifierCache.ts +++ b/src/server/moduleSpecifierCache.ts @@ -1,14 +1,14 @@ +import * as Debug from "../compiler/debug"; +import { nodeModulesPathPart } from "../compiler/moduleNameResolver"; import { - Debug, FileWatcher, ModulePath, ModuleSpecifierCache, ModuleSpecifierOptions, - nodeModulesPathPart, Path, ResolvedModuleSpecifierInfo, UserPreferences, -} from "./_namespaces/ts"; +} from "../compiler/types"; /** @internal */ export interface ModuleSpecifierResolutionCacheHost { diff --git a/src/server/packageJsonCache.ts b/src/server/packageJsonCache.ts index d1c12c65d9bfb..98d03eca06a87 100644 --- a/src/server/packageJsonCache.ts +++ b/src/server/packageJsonCache.ts @@ -1,15 +1,19 @@ +import * as Debug from "../compiler/debug"; import { combinePaths, - createPackageJsonInfo, - Debug, forEachAncestorDirectory, getDirectoryPath, +} from "../compiler/path"; +import { Path, - ProjectPackageJsonInfo, Ternary, +} from "../compiler/types"; +import { ProjectPackageJsonInfo } from "../services/types"; +import { + createPackageJsonInfo, tryFileExists, -} from "./_namespaces/ts"; -import { ProjectService } from "./_namespaces/ts.server"; +} from "../services/utilities"; +import { ProjectService } from "./editorServices"; /** @internal */ export interface PackageJsonCache { diff --git a/src/server/project.ts b/src/server/project.ts index 129d18a2dc1d7..ff2d6c9bff0f6 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1,158 +1,187 @@ -import * as ts from "./_namespaces/ts"; +import { BuilderState } from "../compiler/builderState"; +import { updateErrorForNoInputFiles } from "../compiler/commandLineParser"; import { addRange, append, - ApplyCodeActionCommandResult, arrayFrom, arrayToMap, - BuilderState, - CachedDirectoryStructureHost, - changesAffectModuleResolution, - clearMap, - cloneCompilerOptions, - closeFileWatcher, - closeFileWatcherOf, + concatenate, + enumerateInsertsAndDeletes, + every, + filter, + flatMap, + forEach, + GetCanonicalFileName, + getOrUpdate, + getStringComparer, + map, + mapDefined, + maybeBind, + noop, + orderedRemoveItem, + returnFalse, + returnTrue, + some, + sort, + sortAndDeduplicate, + startsWith, +} from "../compiler/core"; +import { SortedReadonlyArray } from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { getDeclarationEmitOutputFilePathWorker } from "../compiler/emitterUtilities"; +import { removeFileExtension, resolutionExtensionIsTSOrJson } from "../compiler/extension"; +import { + getAutomaticTypeDirectiveNames, + getEffectiveTypeRoots, + getEntrypointsFromPackageJsonInfo, + ModuleResolutionCache, + PackageJsonInfo, + parsePackageName, + resolvePackageNameToPackageJson, +} from "../compiler/moduleNameResolver"; +import { isDeclarationFileName } from "../compiler/parser"; +import { combinePaths, comparePaths, - CompilerHost, - CompilerOptions, - concatenate, - ConfigFileProgramReloadLevel, containsPath, - createCacheableExportInfoMap, - createLanguageService, + directorySeparator, + fileExtensionIs, + getBaseFileName, + getDirectoryPath, + getNormalizedAbsolutePath, + normalizePath, + normalizeSlashes, + toPath, +} from "../compiler/path"; +import { perfLogger } from "../compiler/perfLogger"; +import { timestamp } from "../compiler/performanceCore"; +import { inferredTypesContainingFile } from "../compiler/program"; +import { changesAffectModuleResolution } from "../compiler/programUtilities"; +import { createResolutionCache, + ResolutionCache, +} from "../compiler/resolutionCache"; +import { createSymlinkCache, - Debug, + SymlinkCache, +} from "../compiler/symlinkCache"; +import { + generateDjb2Hash, + PollingInterval, +} from "../compiler/sys"; +import { tracing } from "../compiler/tracing"; +import { + CompilerHost, + CompilerOptions, Diagnostic, - directorySeparator, - DirectoryStructureHost, DirectoryWatcherCallback, DocumentPositionMapper, - DocumentRegistry, - enumerateInsertsAndDeletes, - every, - explainFiles, - ExportInfoMap, Extension, - fileExtensionIs, FileReference, FileWatcher, FileWatcherCallback, FileWatcherEventKind, - filter, - flatMap, - forEach, - forEachEntry, - forEachKey, - generateDjb2Hash, - getAllowJSCompilerOption, - getAutomaticTypeDirectiveNames, - getBaseFileName, - GetCanonicalFileName, - getDeclarationEmitOutputFilePathWorker, - getDefaultCompilerOptions, - getDefaultLibFileName, - getDefaultLibFilePath, - getDirectoryPath, - getEffectiveTypeRoots, - getEmitDeclarations, - getEntrypointsFromPackageJsonInfo, - getNormalizedAbsolutePath, - getOrUpdate, - getStringComparer, HasInvalidatedLibResolutions, HasInvalidatedResolutions, - HostCancellationToken, - inferredTypesContainingFile, - InstallPackageOptions, - IScriptSnapshot, - isDeclarationFileName, - isExternalModuleNameRelative, - isInsideNodeModules, - JsTyping, - LanguageService, - LanguageServiceHost, - LanguageServiceMode, - map, - mapDefined, - maybeBind, - ModuleResolutionCache, ModuleResolutionHost, - noop, - noopFileWatcher, - normalizePath, - normalizeSlashes, - orderedRemoveItem, - outFile, - PackageJsonAutoImportPreference, - PackageJsonInfo, ParsedCommandLine, - parsePackageName, Path, - perfLogger, - PerformanceEvent, PluginImport, - PollingInterval, Program, - ProjectPackageJsonInfo, ProjectReference, - removeFileExtension, - ResolutionCache, - resolutionExtensionIsTSOrJson, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirectiveWithFailedLookupLocations, - resolvePackageNameToPackageJson, - returnFalse, - returnTrue, ScriptKind, - some, - sort, - sortAndDeduplicate, - SortedReadonlyArray, SourceFile, - SourceMapper, - startsWith, StringLiteralLike, - stripQuotes, StructureIsReused, - SymlinkCache, - ThrottledCancellationToken, - timestamp, - toPath, - tracing, TypeAcquisition, - updateErrorForNoInputFiles, - updateMissingFilePathsWatch, WatchDirectoryFlags, WatchOptions, +} from "../compiler/types"; +import { + clearMap, + closeFileWatcher, + forEachEntry, + forEachKey, + getAllowJSCompilerOption, + getEmitDeclarations, + outFile, + stripQuotes, +} from "../compiler/utilities"; +import { + getDefaultLibFileName, + isExternalModuleNameRelative, +} from "../compiler/utilitiesPublic"; +import { + explainFiles, + noopFileWatcher, WatchType, -} from "./_namespaces/ts"; +} from "../compiler/watch"; +import { + CachedDirectoryStructureHost, + closeFileWatcherOf, + ConfigFileProgramReloadLevel, + DirectoryStructureHost, + updateMissingFilePathsWatch, +} from "../compiler/watchUtilities"; +import * as JsTyping from "../jsTyping/jsTyping"; +import { ActionInvalidate } from "../jsTyping/shared"; +import { DocumentRegistry } from "../services/documentRegistry"; +import { createCacheableExportInfoMap } from "../services/exportInfoMap"; +import { + createLanguageService, + getDefaultCompilerOptions, + getDefaultLibFilePath, + ThrottledCancellationToken, +} from "../services/services"; +import { SourceMapper } from "../services/sourcemaps"; +import { + ApplyCodeActionCommandResult, + ExportInfoMap, + HostCancellationToken, + InstallPackageOptions, + IScriptSnapshot, + LanguageService, + LanguageServiceHost, + LanguageServiceMode, + PackageJsonAutoImportPreference, + PerformanceEvent, + ProjectPackageJsonInfo, +} from "../services/types"; +import { + cloneCompilerOptions, + isInsideNodeModules, +} from "../services/utilities"; +import * as ts from "./_namespaces/ts"; +import { + FileStats, + forEachResolvedProjectReferenceProject, + projectContainsInfoDirectly, + ProjectReferenceProjectLoadKind, + ProjectService, + updateProjectIfDirty, +} from "./editorServices"; +import { createModuleSpecifierCache } from "./moduleSpecifierCache"; +import * as protocol from "./protocol"; +import { ScriptInfo } from "./scriptInfo"; +import { Session } from "./session"; +import { + ModuleImportResult, + ServerHost, +} from "./types"; +import { TypingsCache } from "./typingsCache"; import { - ActionInvalidate, asNormalizedPath, - createModuleSpecifierCache, emptyArray, Errors, - FileStats, - forEachResolvedProjectReferenceProject, LogLevel, - ModuleImportResult, Msg, NormalizedPath, - projectContainsInfoDirectly, ProjectOptions, - ProjectReferenceProjectLoadKind, - ProjectService, - ScriptInfo, - ServerHost, - Session, toNormalizedPath, - TypingsCache, - updateProjectIfDirty, -} from "./_namespaces/ts.server"; -import * as protocol from "./protocol"; +} from "./utilitiesPublic"; export enum ProjectKind { Inferred, diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 4e4432d0bc2e7..a2128328faf5b 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1,24 +1,26 @@ -import type * as ts from "./_namespaces/ts"; -import type { +import { OutputFile } from "../compiler/builderStatePublic"; +import { MapLike } from "../compiler/corePublic"; +import { CompilerOptionsValue, - EndOfLineState, FileExtensionInfo, + PluginImport, + ProjectReference, + ScriptKind, + TypeAcquisition, +} from "../compiler/types"; +import { + EndOfLineState, HighlightSpanKind, InteractiveRefactorArguments, - MapLike, OutliningSpanKind, - OutputFile, - PluginImport, - ProjectReference, RenameLocation, ScriptElementKind, - ScriptKind, TextChange, TextInsertion, TodoComment, TodoCommentDescriptor, - TypeAcquisition, -} from "./_namespaces/ts"; +} from "../services/types"; +import type * as ts from "./_namespaces/ts"; // Declaration module describing the TypeScript Server protocol diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 114e3f33d3518..26dc9f2a5d540 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -1,57 +1,73 @@ import { assign, clear, - closeFileWatcherOf, + contains, + forEach, + isString, + some, + stringContains, + unorderedRemoveItem, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { hasTSFileExtension } from "../compiler/extension"; +import { + directorySeparator, + getBaseFileName, +} from "../compiler/path"; +import { computeLineAndCharacterOfPosition, computeLineStarts, computePositionOfLineAndCharacter, - contains, - createTextSpanFromBounds, - Debug, - directorySeparator, +} from "../compiler/scanner"; +import { + getLineInfo, + LineInfo, +} from "../compiler/sourcemap"; +import { DocumentPositionMapper, - DocumentRegistryBucketKeyWithMode, - emptyOptions, FileWatcher, FileWatcherEventKind, - forEach, - FormatCodeSettings, - getBaseFileName, - getDefaultFormatCodeSettings, - getLineInfo, - getScriptKindFromFileName, - getSnapshotText, - hasTSFileExtension, - IScriptSnapshot, - isString, - LineInfo, Path, ScriptKind, - ScriptSnapshot, - some, SourceFile, SourceFileLike, - stringContains, TextSpan, - unorderedRemoveItem, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { getScriptKindFromFileName } from "../compiler/utilities"; +import { createTextSpanFromBounds } from "../compiler/utilitiesPublic"; +import { closeFileWatcherOf } from "../compiler/watchUtilities"; +import { + DocumentRegistryBucketKeyWithMode, +} from "../services/documentRegistry"; +import { + emptyOptions, + FormatCodeSettings, + getDefaultFormatCodeSettings, + IScriptSnapshot, + ScriptSnapshot, +} from "../services/types"; +import { getSnapshotText } from "../services/utilities"; +import { maxFileSize } from "./editorServices"; import { - AbsolutePositionAndLineText, ConfiguredProject, - Errors, ExternalProject, InferredProject, isConfiguredProject, isExternalProject, isInferredProject, - maxFileSize, - NormalizedPath, Project, ProjectKind, - ScriptVersionCache, - ServerHost, -} from "./_namespaces/ts.server"; +} from "./project"; import * as protocol from "./protocol"; +import { + AbsolutePositionAndLineText, + ScriptVersionCache, +} from "./scriptVersionCache"; +import { ServerHost } from "./types"; +import { + Errors, + NormalizedPath, +} from "./utilitiesPublic"; /** @internal */ export class TextStorage { diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index af5faff067112..ee5cddabd5409 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -1,18 +1,18 @@ +import * as Debug from "../compiler/debug"; +import { computeLineStarts } from "../compiler/scanner"; +import { + TextChangeRange, + TextSpan, +} from "../compiler/types"; import { collapseTextChangeRangesAcrossMultipleVersions, - computeLineStarts, createTextChangeRange, createTextSpan, - Debug, - IScriptSnapshot, - TextChangeRange, - TextSpan, unchangedTextChangeRange, -} from "./_namespaces/ts"; -import { - emptyArray, -} from "./_namespaces/ts.server"; +} from "../compiler/utilitiesPublic"; +import { IScriptSnapshot } from "../services/types"; import * as protocol from "./protocol"; +import { emptyArray } from "./utilitiesPublic"; const lineCollectionCapacity = 4; diff --git a/src/server/session.ts b/src/server/session.ts index 30a34c906dd8d..9eacb7fc0159e 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1,169 +1,170 @@ +import { EmitOutput } from "../compiler/builderStatePublic"; import { arrayFrom, arrayReverseIterator, + cast, + concatenate, + createQueue, + createSet, + deduplicate, + equateValues, + filter, + find, + first, + firstIterator, + firstOrUndefined, + flatMap, + flatMapToMutable, + identity, + isArray, + isString, + map, + mapDefined, + mapDefinedIterator, + mapIterator, + memoize, + MultiMap, + singleIterator, + some, + startsWith, + stringContains, + toArray, + toFileNameLowerCase, +} from "../compiler/core"; +import { version } from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { removeFileExtension } from "../compiler/extension"; +import { isIdentifier } from "../compiler/factory/nodeTests"; +import { + getEntrypointsFromPackageJsonInfo, + getPackageNameFromTypesPackageName, + getPackageScopeForPath, + getTemporaryModuleResolutionState, + nodeModulesPathPart, + unmangleScopedPackageName, +} from "../compiler/moduleNameResolver"; +import { getNodeModulePathParts } from "../compiler/moduleSpecifiersUtilities"; +import { isDeclarationFileName } from "../compiler/parser"; +import { + getNormalizedAbsolutePath, + normalizePath, +} from "../compiler/path"; +import { perfLogger } from "../compiler/perfLogger"; +import { flattenDiagnosticMessageText } from "../compiler/program"; +import { + computeLineAndCharacterOfPosition, + computeLineStarts, + getLineAndCharacterOfPosition, +} from "../compiler/scanner"; +import { tracing } from "../compiler/tracing"; +import { BufferEncoding, + CompilerOptions, + Diagnostic, + diagnosticCategoryName, + DiagnosticRelatedInformation, + DocumentPosition, + ImportSpecifier, + LineAndCharacter, + ModuleResolutionKind, + OperationCanceledException, + Path, + Program, + ScriptKind, + SourceFile, + SyntaxKind, + TextRange, + TextSpan, +} from "../compiler/types"; +import { + forEachNameInAccessChainWalkingLeft, + getDeclarationFromName, + getDeclarationOfKind, + getEmitDeclarations, + getTextOfIdentifierOrLiteral, + isAccessExpression, + outFile, +} from "../compiler/utilities"; +import { + createTextSpan, + createTextSpanFromBounds, + decodedTextSpanIntersectsWith, + isStringLiteralLike, + textSpanEnd, +} from "../compiler/utilitiesPublic"; +import { Core as FindAllReferences } from "../services/findAllReferences"; +import { getIndentationString } from "../services/formatting/formatting"; +import { createDefinitionInfo } from "../services/goToDefinition"; +import { + displayPartsToString, + getSupportedCodeFixes, +} from "../services/services"; +import { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, - cast, CodeAction, CodeActionCommand, CodeFixAction, CombinedCodeActions, - CompilerOptions, CompletionEntry, CompletionEntryData, CompletionEntryDetails, CompletionInfo, CompletionTriggerKind, - computeLineAndCharacterOfPosition, - computeLineStarts, - concatenate, - createQueue, - createSet, - createTextSpan, - createTextSpanFromBounds, - Debug, - decodedTextSpanIntersectsWith, - deduplicate, DefinitionInfo, DefinitionInfoAndBoundSpan, - Diagnostic, - diagnosticCategoryName, - DiagnosticRelatedInformation, - displayPartsToString, DocumentHighlights, - DocumentPosition, DocumentSpan, - documentSpansEqual, - EmitOutput, - equateValues, FileTextChanges, - filter, - find, - FindAllReferences, - first, - firstIterator, - firstOrUndefined, - flatMap, - flatMapToMutable, - flattenDiagnosticMessageText, - forEachNameInAccessChainWalkingLeft, FormatCodeSettings, - formatting, - getDeclarationFromName, - getDeclarationOfKind, - getEmitDeclarations, - getEntrypointsFromPackageJsonInfo, - getLineAndCharacterOfPosition, - getMappedContextSpan, - getMappedDocumentSpan, - getMappedLocation, - getNodeModulePathParts, - getNormalizedAbsolutePath, - getPackageNameFromTypesPackageName, - getPackageScopeForPath, - getSnapshotText, - getSupportedCodeFixes, - getTemporaryModuleResolutionState, - getTextOfIdentifierOrLiteral, - getTouchingPropertyName, - GoToDefinition, HostCancellationToken, - identity, ImplementationLocation, - ImportSpecifier, - isAccessExpression, - isArray, - isDeclarationFileName, - isIdentifier, - isString, - isStringLiteralLike, JSDocLinkDisplayPart, JSDocTagInfo, LanguageServiceMode, - LineAndCharacter, LinkedEditingInfo, - map, - mapDefined, - mapDefinedIterator, - mapIterator, - mapOneOrMany, - memoize, - ModuleResolutionKind, - MultiMap, NavigateToItem, NavigationBarItem, NavigationTree, - nodeModulesPathPart, - normalizePath, - OperationCanceledException, OrganizeImportsMode, - outFile, OutliningSpan, - Path, - perfLogger, PerformanceEvent, - PossibleProgramFileInfo, - Program, QuickInfo, RefactorEditInfo, ReferencedSymbol, ReferencedSymbolDefinitionInfo, ReferencedSymbolEntry, ReferenceEntry, - removeFileExtension, RenameInfo, RenameLocation, - ScriptKind, SelectionRange, SemanticClassificationFormat, SignatureHelpItem, SignatureHelpItems, - singleIterator, - some, - SourceFile, - startsWith, - stringContains, SymbolDisplayPart, - SyntaxKind, TextChange, TextInsertion, - TextRange, - TextSpan, - textSpanEnd, - toArray, - toFileNameLowerCase, - tracing, - unmangleScopedPackageName, - version, WithMetadata, -} from "./_namespaces/ts"; +} from "../services/types"; +import { + documentSpansEqual, + getMappedContextSpan, + getMappedDocumentSpan, + getMappedLocation, + getSnapshotText, + getTouchingPropertyName, + mapOneOrMany, + PossibleProgramFileInfo, +} from "../services/utilities"; import { ConfigFileDiagEvent, - ConfiguredProject, convertFormatOptions, convertScriptKindName, convertUserPreferences, - EmitResult, - emptyArray, - Errors, - GcTimer, - indent, isConfigFile, - isConfiguredProject, - isExternalProject, - isInferredProject, - ITypingsInstaller, LargeFileReferencedEvent, - Logger, - LogLevel, - Msg, - NormalizedPath, - nullTypingsInstaller, - Project, ProjectInfoTelemetryEvent, - ProjectKind, ProjectLanguageServiceStateEvent, ProjectLoadingFinishEvent, ProjectLoadingStartEvent, @@ -172,14 +173,36 @@ import { ProjectServiceEventHandler, ProjectServiceOptions, ProjectsUpdatedInBackgroundEvent, - ScriptInfo, ScriptInfoOrConfig, - ServerHost, - stringifyIndented, - toNormalizedPath, updateProjectIfDirty, -} from "./_namespaces/ts.server"; +} from "./editorServices"; +import { + ConfiguredProject, + EmitResult, + isConfiguredProject, + isExternalProject, + isInferredProject, + Project, + ProjectKind, +} from "./project"; import * as protocol from "./protocol"; +import { ScriptInfo } from "./scriptInfo"; +import { ServerHost } from "./types"; +import { ITypingsInstaller, nullTypingsInstaller } from "./typingsCache"; +import { + GcTimer, + indent, + stringifyIndented, +} from "./utilities"; +import { + emptyArray, + Errors, + Logger, + LogLevel, + Msg, + NormalizedPath, + toNormalizedPath, +} from "./utilitiesPublic"; interface StackTraceError extends Error { stack?: string; @@ -1632,14 +1655,14 @@ export class Session implements EventSender { } function searchForDeclaration(declarationName: string, fileToSearch: SourceFile, noDtsProgram: Program) { - const matches = FindAllReferences.Core.getTopMostDeclarationNamesInFile(declarationName, fileToSearch); + const matches = FindAllReferences.getTopMostDeclarationNamesInFile(declarationName, fileToSearch); return mapDefined(matches, match => { const symbol = noDtsProgram.getTypeChecker().getSymbolAtLocation(match); const decl = getDeclarationFromName(match); if (symbol && decl) { // I think the last argument to this is supposed to be the start node, but it doesn't seem important. // Callers internal to GoToDefinition already get confused about this. - return GoToDefinition.createDefinitionInfo(decl, noDtsProgram.getTypeChecker(), symbol, decl, /*unverified*/ true); + return createDefinitionInfo(decl, noDtsProgram.getTypeChecker(), symbol, decl, /*unverified*/ true); } }); } @@ -2218,7 +2241,7 @@ export class Session implements EventSender { const firstNoWhiteSpacePosition = absolutePosition + i; edits.push({ span: createTextSpanFromBounds(absolutePosition, firstNoWhiteSpacePosition), - newText: formatting.getIndentationString(preferredIndent, formatOptions) + newText: getIndentationString(preferredIndent, formatOptions) }); } } diff --git a/src/server/types.ts b/src/server/types.ts index 8e1c38f9a42a3..383425b34f6dd 100644 --- a/src/server/types.ts +++ b/src/server/types.ts @@ -4,7 +4,7 @@ import { FileWatcherCallback, System, WatchOptions, -} from "./_namespaces/ts"; +} from "../compiler/types"; export interface CompressedData { length: number; diff --git a/src/server/typingsCache.ts b/src/server/typingsCache.ts index 42c91be6047d1..4d4906a4756fb 100644 --- a/src/server/typingsCache.ts +++ b/src/server/typingsCache.ts @@ -1,22 +1,24 @@ import { - ApplyCodeActionCommandResult, arrayIsEqualTo, - CompilerOptions, - getAllowJSCompilerOption, - InstallPackageOptions, noop, notImplemented, - Path, returnFalse, sort, - SortedReadonlyArray, +} from "../compiler/core"; +import { SortedReadonlyArray } from "../compiler/corePublic"; +import { + CompilerOptions, + Path, TypeAcquisition, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { getAllowJSCompilerOption } from "../compiler/utilities"; import { - emptyArray, - Project, - ProjectService, -} from "./_namespaces/ts.server"; + ApplyCodeActionCommandResult, + InstallPackageOptions, +} from "../services/types"; +import { ProjectService } from "./editorServices"; +import { Project } from "./project"; +import { emptyArray } from "./utilitiesPublic"; export interface InstallPackageOptionsWithProject extends InstallPackageOptions { projectName: string; diff --git a/src/server/utilities.ts b/src/server/utilities.ts index c9bd0ea1c6700..059cb00310158 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -1,17 +1,19 @@ import { binarySearch, - Comparer, - getBaseFileName, identity, - perfLogger, +} from "../compiler/core"; +import { + Comparer, SortedArray, -} from "./_namespaces/ts"; +} from "../compiler/corePublic"; +import { getBaseFileName } from "../compiler/path"; +import { perfLogger } from "../compiler/perfLogger"; +import { ServerHost } from "./types"; import { Logger, LogLevel, NormalizedPath, - ServerHost, -} from "./_namespaces/ts.server"; +} from "./utilitiesPublic"; /** @internal */ export class ThrottledOperations { diff --git a/src/server/utilitiesPublic.ts b/src/server/utilitiesPublic.ts index b6b6b8d11f7bf..5aa3dd1b57bc5 100644 --- a/src/server/utilitiesPublic.ts +++ b/src/server/utilitiesPublic.ts @@ -1,16 +1,18 @@ +import { + SortedArray, + SortedReadonlyArray, +} from "../compiler/corePublic"; import { getNormalizedAbsolutePath, isRootedDiskPath, normalizePath, +} from "../compiler/path"; +import { Path, - SortedArray, - SortedReadonlyArray, TypeAcquisition, -} from "./_namespaces/ts"; -import { - DiscoverTypings, - Project, -} from "./_namespaces/ts.server"; +} from "../compiler/types"; +import { DiscoverTypings } from "../jsTyping/types"; +import { Project } from "./project"; export enum LogLevel { terse, diff --git a/src/services/_namespaces/ts.codefix.ts b/src/services/_namespaces/ts.codefix.ts index 3bf0c18cb9e5a..082a7aca34b2a 100644 --- a/src/services/_namespaces/ts.codefix.ts +++ b/src/services/_namespaces/ts.codefix.ts @@ -21,6 +21,7 @@ export * from "../codefixes/convertTypedefToType"; export * from "../codefixes/convertLiteralTypeToMappedType"; export * from "../codefixes/fixClassIncorrectlyImplementsInterface"; export * from "../codefixes/importFixes"; +export * from "../codefixes/importAdder"; export * from "../codefixes/fixAddMissingConstraint"; export * from "../codefixes/fixOverrideModifier"; export * from "../codefixes/fixNoPropertyAccessFromIndexSignature"; diff --git a/src/services/_namespaces/ts.ts b/src/services/_namespaces/ts.ts index eae114fd2e834..484c4fbc1a4d4 100644 --- a/src/services/_namespaces/ts.ts +++ b/src/services/_namespaces/ts.ts @@ -17,6 +17,8 @@ export * from "../transpile"; export * from "../services"; export * from "../transform"; export * from "../shims"; +export * from "./ts.textChanges"; +export * from "./ts.formatting"; import * as BreakpointResolver from "./ts.BreakpointResolver"; export { BreakpointResolver }; import * as CallHierarchy from "./ts.CallHierarchy"; @@ -53,7 +55,3 @@ import * as SmartSelectionRange from "./ts.SmartSelectionRange"; export { SmartSelectionRange }; import * as SymbolDisplay from "./ts.SymbolDisplay"; export { SymbolDisplay }; -import * as textChanges from "./ts.textChanges"; -export { textChanges }; -import * as formatting from "./ts.formatting"; -export { formatting }; diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index f06f85142b862..4ab478590f434 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -1,16 +1,29 @@ +import { + getModuleInstanceState, + ModuleInstanceState, +} from "../compiler/binder"; +import { + findLast, + forEach, + lastOrUndefined, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { + isDecorator, + isVariableDeclarationList, +} from "../compiler/factory/nodeTests"; +import { canHaveDecorators } from "../compiler/factory/utilitiesPublic"; +import { skipTrivia } from "../compiler/scanner"; import { ArrayLiteralExpression, BinaryExpression, BindingPattern, Block, BreakOrContinueStatement, - canHaveDecorators, CaseBlock, CaseOrDefaultClause, CatchClause, ClassDeclaration, - createTextSpanFromBounds, - Debug, DestructuringPattern, DoStatement, EnumDeclaration, @@ -18,35 +31,17 @@ import { ExportDeclaration, Expression, ExpressionStatement, - findLast, - findNextToken, - findPrecedingToken, - forEach, ForInStatement, ForOfStatement, ForStatement, FunctionLikeDeclaration, - getModuleInstanceState, - getTokenAtPosition, HasDecorators, - hasOnlyExpressionInitializer, - hasSyntacticModifier, IfStatement, ImportDeclaration, ImportEqualsDeclaration, - isArrayLiteralOrObjectLiteralDestructuringPattern, - isAssignmentOperator, - isBindingPattern, - isDecorator, - isExpressionNode, - isFunctionBlock, - isFunctionLike, - isVariableDeclarationList, LabeledStatement, - lastOrUndefined, ModifierFlags, ModuleDeclaration, - ModuleInstanceState, Node, NodeArray, NodeFlags, @@ -57,7 +52,6 @@ import { PropertyDeclaration, PropertySignature, ReturnStatement, - skipTrivia, SourceFile, SwitchStatement, SyntaxKind, @@ -70,7 +64,25 @@ import { VariableStatement, WhileStatement, WithStatement, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + hasSyntacticModifier, + isAssignmentOperator, + isExpressionNode, + isFunctionBlock, +} from "../compiler/utilities"; +import { + createTextSpanFromBounds, + hasOnlyExpressionInitializer, + isBindingPattern, + isFunctionLike, +} from "../compiler/utilitiesPublic"; +import { + findNextToken, + findPrecedingToken, + getTokenAtPosition, + isArrayLiteralOrObjectLiteralDestructuringPattern, +} from "./utilities"; /** * Get the breakpoint span in given sourceFile diff --git a/src/services/callHierarchy.ts b/src/services/callHierarchy.ts index a2d32e6da717f..677dd918a27a0 100644 --- a/src/services/callHierarchy.ts +++ b/src/services/callHierarchy.ts @@ -1,90 +1,62 @@ import { - AccessExpression, append, - ArrowFunction, - AsExpression, - CallExpression, - CallHierarchyIncomingCall, - CallHierarchyItem, - CallHierarchyOutgoingCall, - CancellationToken, - canHaveModifiers, - ClassDeclaration, - ClassExpression, - ClassLikeDeclaration, - ClassStaticBlockDeclaration, compareStringsCaseSensitive, - createPrinterWithRemoveCommentsOmitTrailingSemicolon, - createTextRangeFromNode, - createTextSpanFromBounds, - createTextSpanFromRange, - Debug, - Decorator, - ElementAccessExpression, - EmitHint, filter, find, - FindAllReferences, - findAncestor, forEach, - forEachChild, - FunctionDeclaration, - FunctionExpression, - FunctionLikeDeclaration, - GetAccessorDeclaration, - getAssignedName, - getClassExtendsHeritageElement, - getCombinedNodeFlags, - getFirstConstructorWithBody, - getNameOfDeclaration, - getNodeId, - getNodeKind, - getNodeModifiers, group, - hasSyntacticModifier, - Identifier, - idText, indicesOf, - isAccessExpression, - isArgumentExpressionOfElementAccess, isArray, + map, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { createPrinterWithRemoveCommentsOmitTrailingSemicolon } from "../compiler/emitter"; +import { isArrowFunction, - isCallOrNewExpressionTarget, isClassDeclaration, isClassExpression, - isClassLike, isClassStaticBlockDeclaration, isComputedPropertyName, isConstructorDeclaration, - isDeclarationName, - isDecoratorTarget, isFunctionDeclaration, isFunctionExpression, - isFunctionLikeDeclaration, isGetAccessorDeclaration, isIdentifier, - isJsxOpeningLikeElement, - isJsxOpeningLikeElementTagName, isMethodDeclaration, isMethodSignature, isModuleBlock, isModuleDeclaration, - isNamedDeclaration, - isPartOfTypeNode, isPropertyDeclaration, - isRightSideOfPropertyAccess, isSetAccessorDeclaration, isSourceFile, - isStringOrNumericLiteralLike, isTaggedTemplateExpression, - isTaggedTemplateTag, isVariableDeclaration, +} from "../compiler/factory/nodeTests"; +import { canHaveModifiers } from "../compiler/factory/utilitiesPublic"; +import { forEachChild } from "../compiler/parser"; +import { skipTrivia } from "../compiler/scanner"; +import { + AccessExpression, + ArrowFunction, + AsExpression, + CallExpression, + CancellationToken, + ClassDeclaration, + ClassExpression, + ClassLikeDeclaration, + ClassStaticBlockDeclaration, + Decorator, + ElementAccessExpression, + EmitHint, + FunctionDeclaration, + FunctionExpression, + FunctionLikeDeclaration, + GetAccessorDeclaration, + Identifier, JsxOpeningLikeElement, - map, MethodDeclaration, ModifierFlags, ModuleDeclaration, - moveRangePastModifiers, NewExpression, Node, NodeFlags, @@ -93,7 +65,6 @@ import { PropertyAccessExpression, SatisfiesExpression, SetAccessorDeclaration, - skipTrivia, SourceFile, SymbolFlags, SyntaxKind, @@ -102,9 +73,55 @@ import { TextSpan, TypeAssertion, TypeChecker, - usingSingleLineStringWriter, VariableDeclaration, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + getClassExtendsHeritageElement, + getFirstConstructorWithBody, + getNodeId, + hasSyntacticModifier, + isAccessExpression, + isDeclarationName, + isPartOfTypeNode, + isStringOrNumericLiteralLike, + moveRangePastModifiers, + usingSingleLineStringWriter, +} from "../compiler/utilities"; +import { + createTextSpanFromBounds, + findAncestor, + getAssignedName, + getCombinedNodeFlags, + getNameOfDeclaration, + idText, + isClassLike, + isFunctionLikeDeclaration, + isJsxOpeningLikeElement, + isNamedDeclaration, +} from "../compiler/utilitiesPublic"; +import { + findReferenceOrRenameEntries, + FindReferencesUse, +} from "./findAllReferences"; +import { + CallHierarchyIncomingCall, + CallHierarchyItem, + CallHierarchyOutgoingCall, + Entry, + EntryKind, +} from "./types"; +import { + createTextRangeFromNode, + createTextSpanFromRange, + getNodeKind, + getNodeModifiers, + isArgumentExpressionOfElementAccess, + isCallOrNewExpressionTarget, + isDecoratorTarget, + isJsxOpeningLikeElementTagName, + isRightSideOfPropertyAccess, + isTaggedTemplateTag, +} from "./utilities"; /** @internal */ export type NamedExpression = @@ -429,8 +446,8 @@ interface CallSite { range: TextRange; } -function convertEntryToCallSite(entry: FindAllReferences.Entry): CallSite | undefined { - if (entry.kind === FindAllReferences.EntryKind.Node) { +function convertEntryToCallSite(entry: Entry): CallSite | undefined { + if (entry.kind === EntryKind.Node) { const { node } = entry; if (isCallOrNewExpressionTarget(node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true) || isTaggedTemplateTag(node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true) @@ -468,7 +485,7 @@ export function getIncomingCalls(program: Program, declaration: CallHierarchyDec return []; } const location = getCallHierarchyDeclarationReferenceNode(declaration); - const calls = filter(FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, program.getSourceFiles(), location, /*position*/ 0, { use: FindAllReferences.FindReferencesUse.References }, convertEntryToCallSite), isDefined); + const calls = filter(findReferenceOrRenameEntries(program, cancellationToken, program.getSourceFiles(), location, /*position*/ 0, { use: FindReferencesUse.References }, convertEntryToCallSite), isDefined); return calls ? group(calls, getCallSiteGroupKey, entries => convertCallSiteGroupToIncomingCall(program, entries)) : []; } diff --git a/src/services/classifier.ts b/src/services/classifier.ts index 9e2a34583e5cb..7ee8c641b5af7 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -1,41 +1,35 @@ import { - __String, + getModuleInstanceState, + ModuleInstanceState, +} from "../compiler/binder"; +import { arrayToNumericMap, + isLineBreak, + lastOrUndefined, + some, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { + isIdentifier, + isJSDoc, + isModuleDeclaration, +} from "../compiler/factory/nodeTests"; +import { parseIsolatedJSDocComment } from "../compiler/parser"; +import { setParent } from "../compiler/parserUtilities"; +import { + couldStartTrivia, + createScanner, + Scanner, +} from "../compiler/scanner"; +import { + __String, CancellationToken, CharacterCodes, ClassDeclaration, - ClassificationInfo, - ClassificationResult, - Classifications, - ClassificationType, - ClassificationTypeNames, - ClassifiedSpan, - Classifier, commentPragmas, - couldStartTrivia, - createScanner, - createTextSpan, - Debug, - decodedTextSpanIntersectsWith, - EndOfLineState, EnumDeclaration, - getMeaningFromLocation, - getModuleInstanceState, - getTypeArgumentOrTypeParameterList, HasJSDoc, InterfaceDeclaration, - isAccessibilityModifier, - isConstTypeReference, - isIdentifier, - isJSDoc, - isKeyword, - isLineBreak, - isModuleDeclaration, - isPunctuation, - isTemplateLiteralKind, - isThisIdentifier, - isToken, - isTrivia, JSDoc, JSDocAugmentsTag, JSDocCallbackTag, @@ -54,28 +48,50 @@ import { JsxClosingElement, JsxOpeningElement, JsxSelfClosingElement, - lastOrUndefined, ModuleDeclaration, - ModuleInstanceState, Node, - nodeIsMissing, ParameterDeclaration, - parseIsolatedJSDocComment, - Scanner, ScriptTarget, - SemanticMeaning, - setParent, - some, SourceFile, Symbol, SymbolFlags, SyntaxKind, TextSpan, - textSpanIntersectsWith, - TokenClass, TypeChecker, TypeParameterDeclaration, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + isKeyword, + isPunctuation, + isThisIdentifier, + isTrivia, + nodeIsMissing, +} from "../compiler/utilities"; +import { + createTextSpan, + decodedTextSpanIntersectsWith, + isConstTypeReference, + isTemplateLiteralKind, + isToken, + textSpanIntersectsWith, +} from "../compiler/utilitiesPublic"; +import { + ClassificationInfo, + ClassificationResult, + Classifications, + ClassificationType, + ClassificationTypeNames, + ClassifiedSpan, + Classifier, + EndOfLineState, + TokenClass, +} from "./types"; +import { + getMeaningFromLocation, + getTypeArgumentOrTypeParameterList, + isAccessibilityModifier, + SemanticMeaning, +} from "./utilities"; /** The classifier is used for syntactic highlighting in editors via the TSServer */ export function createClassifier(): Classifier { diff --git a/src/services/classifier2020.ts b/src/services/classifier2020.ts index 96e246a85db80..863b92943fdbb 100644 --- a/src/services/classifier2020.ts +++ b/src/services/classifier2020.ts @@ -1,16 +1,5 @@ +import * as Debug from "../compiler/debug"; import { - BindingElement, - CancellationToken, - Classifications, - ClassifiedSpan2020, - createTextSpan, - Debug, - Declaration, - EndOfLineState, - forEachChild, - getCombinedModifierFlags, - getCombinedNodeFlags, - getMeaningFromLocation, isBindingElement, isCallExpression, isCatchClause, @@ -18,7 +7,6 @@ import { isIdentifier, isImportClause, isImportSpecifier, - isInfinityOrNaNString, isJsxElement, isJsxExpression, isJsxSelfClosingElement, @@ -27,23 +15,43 @@ import { isQualifiedName, isSourceFile, isVariableDeclaration, +} from "../compiler/factory/nodeTests"; +import { forEachChild } from "../compiler/parser"; +import { + BindingElement, + CancellationToken, + Declaration, ModifierFlags, NamedDeclaration, Node, NodeFlags, ParameterDeclaration, Program, - SemanticMeaning, SourceFile, Symbol, SymbolFlags, SyntaxKind, TextSpan, - textSpanIntersectsWith, Type, TypeChecker, VariableDeclaration, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { isInfinityOrNaNString } from "../compiler/utilities"; +import { + createTextSpan, + getCombinedModifierFlags, + getCombinedNodeFlags, + textSpanIntersectsWith, +} from "../compiler/utilitiesPublic"; +import { + Classifications, + ClassifiedSpan2020, + EndOfLineState, +} from "./types"; +import { + getMeaningFromLocation, + SemanticMeaning, +} from "./utilities"; /** @internal */ export const enum TokenEncodingConsts { diff --git a/src/services/codeFixProvider.ts b/src/services/codeFixProvider.ts index 67dbdbc9a9280..6dac9b0e26d1f 100644 --- a/src/services/codeFixProvider.ts +++ b/src/services/codeFixProvider.ts @@ -1,6 +1,20 @@ import { arrayFrom, cast, + contains, + createMultiMap, + flatMap, + isString, + map, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { + Diagnostic, + DiagnosticWithLocation, +} from "../compiler/types"; +import { computeSuggestionDiagnostics } from "./suggestionDiagnostics"; +import { ChangeTracker } from "./textChanges"; +import { CodeActionCommand, CodeFixAction, CodeFixAllContext, @@ -8,21 +22,13 @@ import { CodeFixContextBase, CodeFixRegistration, CombinedCodeActions, - computeSuggestionDiagnostics, - contains, - createMultiMap, - Debug, - Diagnostic, - DiagnosticOrDiagnosticAndArguments, - diagnosticToString, - DiagnosticWithLocation, FileTextChanges, - flatMap, - isString, - map, TextChange, - textChanges, -} from "./_namespaces/ts"; +} from "./types"; +import { + DiagnosticOrDiagnosticAndArguments, + diagnosticToString, +} from "./utilities"; const errorCodeToFixes = createMultiMap(); const fixIdToRegistration = new Map(); @@ -107,10 +113,10 @@ export function createFileTextChanges(fileName: string, textChanges: TextChange[ export function codeFixAll( context: CodeFixAllContext, errorCodes: number[], - use: (changes: textChanges.ChangeTracker, error: DiagnosticWithLocation, commands: CodeActionCommand[]) => void, + use: (changes: ChangeTracker, error: DiagnosticWithLocation, commands: CodeActionCommand[]) => void, ): CombinedCodeActions { const commands: CodeActionCommand[] = []; - const changes = textChanges.ChangeTracker.with(context, t => eachDiagnostic(context, errorCodes, diag => use(t, diag, commands))); + const changes = ChangeTracker.with(context, t => eachDiagnostic(context, errorCodes, diag => use(t, diag, commands))); return createCombinedCodeActions(changes, commands.length === 0 ? undefined : commands); } diff --git a/src/services/codefixes/addConvertToUnknownForNonOverlappingTypes.ts b/src/services/codefixes/addConvertToUnknownForNonOverlappingTypes.ts index 0360e9d117a52..325bf023450a5 100644 --- a/src/services/codefixes/addConvertToUnknownForNonOverlappingTypes.ts +++ b/src/services/codefixes/addConvertToUnknownForNonOverlappingTypes.ts @@ -1,22 +1,24 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - AsExpression, - Diagnostics, - factory, - findAncestor, - getTokenAtPosition, isAsExpression, - isInJSFile, isTypeAssertionExpression, +} from "../../compiler/factory/nodeTests"; +import { + AsExpression, SourceFile, SyntaxKind, - textChanges, TypeAssertion, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { isInJSFile } from "../../compiler/utilities"; +import { findAncestor } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "addConvertToUnknownForNonOverlappingTypes"; const errorCodes = [Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first.code]; @@ -25,7 +27,7 @@ registerCodeFix({ getCodeActions: function getCodeActionsToAddConvertToUnknownForNonOverlappingTypes(context) { const assertion = getAssertion(context.sourceFile, context.span.start); if (assertion === undefined) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, assertion)); + const changes = ChangeTracker.with(context, t => makeChange(t, context.sourceFile, assertion)); return [createCodeFixAction(fixId, changes, Diagnostics.Add_unknown_conversion_for_non_overlapping_types, fixId, Diagnostics.Add_unknown_to_all_conversions_of_non_overlapping_types)]; }, fixIds: [fixId], @@ -37,7 +39,7 @@ registerCodeFix({ }), }); -function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, assertion: AsExpression | TypeAssertion) { +function makeChange(changeTracker: ChangeTracker, sourceFile: SourceFile, assertion: AsExpression | TypeAssertion) { const replacement = isAsExpression(assertion) ? factory.createAsExpression(assertion.expression, factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword)) : factory.createTypeAssertion(factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword), assertion.expression); diff --git a/src/services/codefixes/addEmptyExportDeclaration.ts b/src/services/codefixes/addEmptyExportDeclaration.ts index d4020fea83f7f..304396ecb6fe8 100644 --- a/src/services/codefixes/addEmptyExportDeclaration.ts +++ b/src/services/codefixes/addEmptyExportDeclaration.ts @@ -1,12 +1,10 @@ -import { - Diagnostics, - factory, - textChanges, -} from "../_namespaces/ts"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { createCodeFixActionWithoutFixAll, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; registerCodeFix({ errorCodes: [ @@ -15,7 +13,7 @@ registerCodeFix({ ], getCodeActions: function getCodeActionsToAddEmptyExportDeclaration(context) { const { sourceFile } = context; - const changes = textChanges.ChangeTracker.with(context, changes => { + const changes = ChangeTracker.with(context, changes => { const exportDeclaration = factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, @@ -26,4 +24,4 @@ registerCodeFix({ }); return [createCodeFixActionWithoutFixAll("addEmptyExportDeclaration", changes, Diagnostics.Add_export_to_make_this_file_into_a_module)]; }, -}); \ No newline at end of file +}); diff --git a/src/services/codefixes/addMissingAsync.ts b/src/services/codefixes/addMissingAsync.ts index bb0510a90d0ad..c87717202ce37 100644 --- a/src/services/codefixes/addMissingAsync.ts +++ b/src/services/codefixes/addMissingAsync.ts @@ -1,41 +1,51 @@ import { - ArrowFunction, - CodeFixAllContext, - CodeFixContext, - createTextSpanFromNode, - Diagnostic, - Diagnostics, - factory, - FileTextChanges, find, - findAncestor, - FunctionDeclaration, - FunctionExpression, - getNodeId, - getSyntacticModifierFlags, - getSynthesizedDeepClone, - getTokenAtPosition, + isNumber, + some, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, isFunctionDeclaration, isFunctionExpression, isMethodDeclaration, - isNumber, +} from "../../compiler/factory/nodeTests"; +import { + ArrowFunction, + Diagnostic, + FunctionDeclaration, + FunctionExpression, MethodDeclaration, ModifierFlags, - some, SourceFile, - textChanges, TextSpan, +} from "../../compiler/types"; +import { getNodeId, getSyntacticModifierFlags } from "../../compiler/utilities"; +import { + findAncestor, textSpanEnd, - textSpansEqual, -} from "../_namespaces/ts"; +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + CodeFixAllContext, + CodeFixContext, + FileTextChanges, +} from "../types"; +import { + createTextSpanFromNode, + getSynthesizedDeepClone, + getTokenAtPosition, + textSpansEqual, +} from "../utilities"; + +type ContextualTrackChangesFunction = (cb: (changeTracker: ChangeTracker) => void) => FileTextChanges[]; -type ContextualTrackChangesFunction = (cb: (changeTracker: textChanges.ChangeTracker) => void) => FileTextChanges[]; const fixId = "addMissingAsync"; const errorCodes = [ Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code, @@ -56,7 +66,7 @@ registerCodeFix({ return; } - const trackChanges: ContextualTrackChangesFunction = cb => textChanges.ChangeTracker.with(context, cb); + const trackChanges: ContextualTrackChangesFunction = cb => ChangeTracker.with(context, cb); return [getFix(context, decl, trackChanges)]; }, getAllCodeActions: context => { @@ -80,7 +90,7 @@ function getFix(context: CodeFixContext | CodeFixAllContext, decl: FixableDeclar return createCodeFixAction(fixId, changes, Diagnostics.Add_async_modifier_to_containing_function, fixId, Diagnostics.Add_all_missing_async_modifiers); } -function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, insertionSite: FixableDeclaration, fixedDeclarations?: Set) { +function makeChange(changeTracker: ChangeTracker, sourceFile: SourceFile, insertionSite: FixableDeclaration, fixedDeclarations?: Set) { if (fixedDeclarations) { if (fixedDeclarations.has(getNodeId(insertionSite))) { return; diff --git a/src/services/codefixes/addMissingAwait.ts b/src/services/codefixes/addMissingAwait.ts index d740ccac28f5b..c099606b57bb8 100644 --- a/src/services/codefixes/addMissingAwait.ts +++ b/src/services/codefixes/addMissingAwait.ts @@ -1,58 +1,70 @@ import { - CancellationToken, - CodeFixAllContext, - CodeFixContext, compact, contains, - Diagnostic, - Diagnostics, - Expression, - factory, - FileTextChanges, find, - FindAllReferences, - findAncestor, - findPrecedingToken, forEach, - getAncestor, - getFixableErrorSpanExpression, - getSymbolId, - hasSyntacticModifier, - Identifier, + isNumber, + some, + tryAddToSet, + tryCast, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, isBinaryExpression, isBlock, - isCallOrNewExpression, isForOfStatement, isIdentifier, - isNumber, isPropertyAccessExpression, isVariableDeclaration, +} from "../../compiler/factory/nodeTests"; +import { + CancellationToken, + Diagnostic, + Expression, + Identifier, ModifierFlags, Node, NodeFlags, - positionIsASICandidate, Program, - some, SourceFile, Symbol, SyntaxKind, - textChanges, TextSpan, - textSpansEqual, - tryAddToSet, - tryCast, TypeChecker, TypeFlags, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getAncestor, + getSymbolId, + hasSyntacticModifier, +} from "../../compiler/utilities"; +import { + findAncestor, + isCallOrNewExpression, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, createCodeFixActionWithoutFixAll, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { Core as FindAllReferences } from "../findAllReferences"; +import { ChangeTracker } from "../textChanges"; +import { + CodeFixAllContext, + CodeFixContext, + FileTextChanges, +} from "../types"; +import { + findPrecedingToken, + getFixableErrorSpanExpression, + positionIsASICandidate, + textSpansEqual, +} from "../utilities"; -type ContextualTrackChangesFunction = (cb: (changeTracker: textChanges.ChangeTracker) => void) => FileTextChanges[]; +type ContextualTrackChangesFunction = (cb: (changeTracker: ChangeTracker) => void) => FileTextChanges[]; const fixId = "addMissingAwait"; const propertyAccessCode = Diagnostics.Property_0_does_not_exist_on_type_1.code; const callableConstructableErrorCodes = [ @@ -90,7 +102,7 @@ registerCodeFix({ } const checker = context.program.getTypeChecker(); - const trackChanges: ContextualTrackChangesFunction = cb => textChanges.ChangeTracker.with(context, cb); + const trackChanges: ContextualTrackChangesFunction = cb => ChangeTracker.with(context, cb); return compact([ getDeclarationSiteFix(context, expression, errorCode, checker, trackChanges), getUseSiteFix(context, expression, errorCode, checker, trackChanges)]); @@ -199,7 +211,7 @@ function findAwaitableInitializers( } const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken); - const isUsedElsewhere = FindAllReferences.Core.eachSymbolReferenceInFile(variableName, checker, sourceFile, reference => { + const isUsedElsewhere = FindAllReferences.eachSymbolReferenceInFile(variableName, checker, sourceFile, reference => { return identifier !== reference && !symbolReferenceIsAlsoMissingAwait(reference, diagnostics, sourceFile, checker); }); @@ -277,7 +289,7 @@ function isInsideAwaitableBody(node: Node) { ancestor.parent.kind === SyntaxKind.MethodDeclaration)); } -function makeChange(changeTracker: textChanges.ChangeTracker, errorCode: number, sourceFile: SourceFile, checker: TypeChecker, insertionSite: Expression, fixedDeclarations?: Set) { +function makeChange(changeTracker: ChangeTracker, errorCode: number, sourceFile: SourceFile, checker: TypeChecker, insertionSite: Expression, fixedDeclarations?: Set) { if (isForOfStatement(insertionSite.parent) && !insertionSite.parent.awaitModifier) { const exprType = checker.getTypeAtLocation(insertionSite); const asyncIter = checker.getAsyncIterableType(); @@ -334,7 +346,7 @@ function makeChange(changeTracker: textChanges.ChangeTracker, errorCode: number, } } -function insertLeadingSemicolonIfNeeded(changeTracker: textChanges.ChangeTracker, beforeNode: Node, sourceFile: SourceFile) { +function insertLeadingSemicolonIfNeeded(changeTracker: ChangeTracker, beforeNode: Node, sourceFile: SourceFile) { const precedingToken = findPrecedingToken(beforeNode.pos, sourceFile); if (precedingToken && positionIsASICandidate(precedingToken.end, precedingToken.parent, sourceFile)) { changeTracker.insertText(sourceFile, beforeNode.getStart(sourceFile), ";"); diff --git a/src/services/codefixes/addMissingConst.ts b/src/services/codefixes/addMissingConst.ts index 8a848c2616f80..a5b1e0a436d38 100644 --- a/src/services/codefixes/addMissingConst.ts +++ b/src/services/codefixes/addMissingConst.ts @@ -1,28 +1,34 @@ import { - Diagnostics, every, - Expression, - findAncestor, - getTokenAtPosition, + tryAddToSet, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { isArrayLiteralExpression, - isAssignmentExpression, isBinaryExpression, isExpressionStatement, - isForInOrOfStatement, isIdentifier, +} from "../../compiler/factory/nodeTests"; +import { + Expression, Node, Program, SourceFile, SyntaxKind, - textChanges, - tryAddToSet, TypeChecker, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { isAssignmentExpression } from "../../compiler/utilities"; +import { + findAncestor, + isForInOrOfStatement, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "addMissingConst"; const errorCodes = [ @@ -33,7 +39,7 @@ const errorCodes = [ registerCodeFix({ errorCodes, getCodeActions: function getCodeActionsToAddMissingConst(context) { - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start, context.program)); + const changes = ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start, context.program)); if (changes.length > 0) { return [createCodeFixAction(fixId, changes, Diagnostics.Add_const_to_unresolved_variable, fixId, Diagnostics.Add_const_to_all_unresolved_variables)]; } @@ -45,7 +51,7 @@ registerCodeFix({ }, }); -function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number, program: Program, fixedNodes?: Set) { +function makeChange(changeTracker: ChangeTracker, sourceFile: SourceFile, pos: number, program: Program, fixedNodes?: Set) { const token = getTokenAtPosition(sourceFile, pos); const forInitializer = findAncestor(token, node => isForInOrOfStatement(node.parent) ? node.parent.initializer === node : @@ -81,7 +87,7 @@ function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: Source } } -function applyChange(changeTracker: textChanges.ChangeTracker, initializer: Node, sourceFile: SourceFile, fixedNodes?: Set) { +function applyChange(changeTracker: ChangeTracker, initializer: Node, sourceFile: SourceFile, fixedNodes?: Set) { if (!fixedNodes || tryAddToSet(fixedNodes, initializer)) { changeTracker.insertModifierBefore(sourceFile, SyntaxKind.ConstKeyword, initializer); } diff --git a/src/services/codefixes/addMissingDeclareProperty.ts b/src/services/codefixes/addMissingDeclareProperty.ts index c46c353b37f3e..fa2e768f800f2 100644 --- a/src/services/codefixes/addMissingDeclareProperty.ts +++ b/src/services/codefixes/addMissingDeclareProperty.ts @@ -1,18 +1,18 @@ +import { tryAddToSet } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { isIdentifier } from "../../compiler/factory/nodeTests"; import { - Diagnostics, - getTokenAtPosition, - isIdentifier, Node, SourceFile, SyntaxKind, - textChanges, - tryAddToSet, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "addMissingDeclareProperty"; const errorCodes = [ @@ -22,7 +22,7 @@ const errorCodes = [ registerCodeFix({ errorCodes, getCodeActions: function getCodeActionsToAddMissingDeclareOnProperty(context) { - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start)); + const changes = ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start)); if (changes.length > 0) { return [createCodeFixAction(fixId, changes, Diagnostics.Prefix_with_declare, fixId, Diagnostics.Prefix_all_incorrect_property_declarations_with_declare)]; } @@ -34,7 +34,7 @@ registerCodeFix({ }, }); -function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number, fixedNodes?: Set) { +function makeChange(changeTracker: ChangeTracker, sourceFile: SourceFile, pos: number, fixedNodes?: Set) { const token = getTokenAtPosition(sourceFile, pos); if (!isIdentifier(token)) { return; diff --git a/src/services/codefixes/addMissingInvocationForDecorator.ts b/src/services/codefixes/addMissingInvocationForDecorator.ts index 330757c9fa76f..2b57e78d884eb 100644 --- a/src/services/codefixes/addMissingInvocationForDecorator.ts +++ b/src/services/codefixes/addMissingInvocationForDecorator.ts @@ -1,32 +1,30 @@ -import { - Debug, - Diagnostics, - factory, - findAncestor, - getTokenAtPosition, - isDecorator, - SourceFile, - textChanges, -} from "../_namespaces/ts"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isDecorator } from "../../compiler/factory/nodeTests"; +import { SourceFile } from "../../compiler/types"; +import { findAncestor } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "addMissingInvocationForDecorator"; const errorCodes = [Diagnostics._0_accepts_too_few_arguments_to_be_used_as_a_decorator_here_Did_you_mean_to_call_it_first_and_write_0.code]; registerCodeFix({ errorCodes, getCodeActions: function getCodeActionsToAddMissingInvocationForDecorator(context) { - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start)); + const changes = ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start)); return [createCodeFixAction(fixId, changes, Diagnostics.Call_decorator_expression, fixId, Diagnostics.Add_to_all_uncalled_decorators)]; }, fixIds: [fixId], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag.start)), }); -function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number) { +function makeChange(changeTracker: ChangeTracker, sourceFile: SourceFile, pos: number) { const token = getTokenAtPosition(sourceFile, pos); const decorator = findAncestor(token, isDecorator)!; Debug.assert(!!decorator, "Expected position to be owned by a decorator."); diff --git a/src/services/codefixes/addNameToNamelessParameter.ts b/src/services/codefixes/addNameToNamelessParameter.ts index dc36931d79237..2f57c3f802d2a 100644 --- a/src/services/codefixes/addNameToNamelessParameter.ts +++ b/src/services/codefixes/addNameToNamelessParameter.ts @@ -1,32 +1,32 @@ +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isParameter } from "../../compiler/factory/nodeTests"; import { - Debug, - Diagnostics, - factory, - getTokenAtPosition, Identifier, - isParameter, SourceFile, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "addNameToNamelessParameter"; const errorCodes = [Diagnostics.Parameter_has_a_name_but_no_type_Did_you_mean_0_Colon_1.code]; registerCodeFix({ errorCodes, getCodeActions: function getCodeActionsToAddNameToNamelessParameter(context) { - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start)); + const changes = ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start)); return [createCodeFixAction(fixId, changes, Diagnostics.Add_parameter_name, fixId, Diagnostics.Add_names_to_all_parameters_without_names)]; }, fixIds: [fixId], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag.start)), }); -function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number) { +function makeChange(changeTracker: ChangeTracker, sourceFile: SourceFile, pos: number) { const token = getTokenAtPosition(sourceFile, pos); const param = token.parent; if (!isParameter(param)) { diff --git a/src/services/codefixes/addOptionalPropertyUndefined.ts b/src/services/codefixes/addOptionalPropertyUndefined.ts index 7664bea35be10..c48087eb426ea 100644 --- a/src/services/codefixes/addOptionalPropertyUndefined.ts +++ b/src/services/codefixes/addOptionalPropertyUndefined.ts @@ -1,14 +1,9 @@ +import { emptyArray } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - Diagnostics, - emptyArray, - factory, - getFixableErrorSpanExpression, - getSourceFileOfNode, - Identifier, isBinaryExpression, isCallExpression, - isExpression, - isFunctionLikeKind, isIdentifier, isPropertyAccessExpression, isPropertyAssignment, @@ -16,21 +11,30 @@ import { isPropertySignature, isShorthandPropertyAssignment, isVariableDeclaration, +} from "../../compiler/factory/nodeTests"; +import { + Identifier, Node, PropertyAccessExpression, SignatureDeclaration, SourceFile, Symbol, SyntaxKind, - textChanges, TextSpan, TypeChecker, UnionTypeNode, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { getSourceFileOfNode } from "../../compiler/utilities"; +import { + isExpression, + isFunctionLikeKind, +} from "../../compiler/utilitiesPublic"; import { createCodeFixActionWithoutFixAll, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getFixableErrorSpanExpression } from "../utilities"; const addOptionalPropertyUndefined = "addOptionalPropertyUndefined"; @@ -48,7 +52,7 @@ registerCodeFix({ if (!toAdd.length) { return undefined; } - const changes = textChanges.ChangeTracker.with(context, t => addUndefinedToOptionalProperty(t, toAdd)); + const changes = ChangeTracker.with(context, t => addUndefinedToOptionalProperty(t, toAdd)); return [createCodeFixActionWithoutFixAll(addOptionalPropertyUndefined, changes, Diagnostics.Add_undefined_to_optional_property_type)]; }, fixIds: [addOptionalPropertyUndefined], @@ -113,7 +117,7 @@ function getSourceTarget(errorNode: Node | undefined, checker: TypeChecker): { s return undefined; } -function addUndefinedToOptionalProperty(changes: textChanges.ChangeTracker, toAdd: Symbol[]) { +function addUndefinedToOptionalProperty(changes: ChangeTracker, toAdd: Symbol[]) { for (const add of toAdd) { const d = add.valueDeclaration; if (d && (isPropertySignature(d) || isPropertyDeclaration(d)) && d.type) { diff --git a/src/services/codefixes/annotateWithTypeFromJSDoc.ts b/src/services/codefixes/annotateWithTypeFromJSDoc.ts index a2cfc5e97224c..6b71b9c3fb191 100644 --- a/src/services/codefixes/annotateWithTypeFromJSDoc.ts +++ b/src/services/codefixes/annotateWithTypeFromJSDoc.ts @@ -1,52 +1,62 @@ import { - Debug, - Diagnostics, - EmitFlags, emptyArray, - factory, - findChildOfKind, first, - FunctionLikeDeclaration, - getJSDocReturnType, - getJSDocType, - getJSDocTypeParameterDeclarations, - getTokenAtPosition, + last, + map, + tryCast, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { setEmitFlags } from "../../compiler/factory/emitNode"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, - isFunctionLikeDeclaration, isIdentifier, - isJSDocIndexSignature, - isOptionalJSDocPropertyLikeTag, isParameter, - isTypeNode, +} from "../../compiler/factory/nodeTests"; +import { nullTransformationContext } from "../../compiler/transformer"; +import { + EmitFlags, JSDocFunctionType, JSDocNonNullableType, JSDocNullableType, JSDocOptionalType, JSDocTypeLiteral, JSDocVariadicType, - last, - map, Node, - nullTransformationContext, ParameterDeclaration, - PropertyDeclaration, - PropertySignature, - setEmitFlags, SourceFile, SyntaxKind, - textChanges, - tryCast, TypeReferenceNode, - VariableDeclaration, +} from "../../compiler/types"; +import { + getJSDocTypeParameterDeclarations, + isJSDocIndexSignature, + isOptionalJSDocPropertyLikeTag, +} from "../../compiler/utilities"; +import { + getJSDocReturnType, + getJSDocType, + isFunctionLikeDeclaration, + isTypeNode, +} from "../../compiler/utilitiesPublic"; +import { visitEachChild, visitNode, visitNodes, -} from "../_namespaces/ts"; +} from "../../compiler/visitorPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + DeclarationWithType, + findChildOfKind, + getTokenAtPosition, + parameterShouldGetTypeFromJSDoc, +} from "../utilities"; const fixId = "annotateWithTypeFromJSDoc"; const errorCodes = [Diagnostics.JSDoc_types_may_be_moved_to_TypeScript_types.code]; @@ -55,7 +65,7 @@ registerCodeFix({ getCodeActions(context) { const decl = getDeclaration(context.sourceFile, context.span.start); if (!decl) return; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, decl)); + const changes = ChangeTracker.with(context, t => doChange(t, context.sourceFile, decl)); return [createCodeFixAction(fixId, changes, Diagnostics.Annotate_with_type_from_JSDoc, fixId, Diagnostics.Annotate_everything_with_types_from_JSDoc)]; }, fixIds: [fixId], @@ -71,25 +81,7 @@ function getDeclaration(file: SourceFile, pos: number): DeclarationWithType | un return tryCast(isParameter(name.parent) ? name.parent.parent : name.parent, parameterShouldGetTypeFromJSDoc); } -/** @internal */ -export type DeclarationWithType = - | FunctionLikeDeclaration - | VariableDeclaration - | PropertySignature - | PropertyDeclaration; - -/** @internal */ -export function parameterShouldGetTypeFromJSDoc(node: Node): node is DeclarationWithType { - return isDeclarationWithType(node) && hasUsableJSDoc(node); -} - -function hasUsableJSDoc(decl: DeclarationWithType | ParameterDeclaration): boolean { - return isFunctionLikeDeclaration(decl) - ? decl.parameters.some(hasUsableJSDoc) || (!decl.type && !!getJSDocReturnType(decl)) - : !decl.type && !!getJSDocType(decl); -} - -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, decl: DeclarationWithType): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, decl: DeclarationWithType): void { if (isFunctionLikeDeclaration(decl) && (getJSDocReturnType(decl) || decl.parameters.some(p => !!getJSDocType(p)))) { if (!decl.typeParameters) { const typeParameters = getJSDocTypeParameterDeclarations(decl); @@ -116,13 +108,6 @@ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, de } } -function isDeclarationWithType(node: Node): node is DeclarationWithType { - return isFunctionLikeDeclaration(node) || - node.kind === SyntaxKind.VariableDeclaration || - node.kind === SyntaxKind.PropertySignature || - node.kind === SyntaxKind.PropertyDeclaration; -} - function transformJSDocType(node: Node): Node { switch (node.kind) { case SyntaxKind.JSDocAllType: diff --git a/src/services/codefixes/convertConstToLet.ts b/src/services/codefixes/convertConstToLet.ts index d9817ab8250b2..2ef8f78507715 100644 --- a/src/services/codefixes/convertConstToLet.ts +++ b/src/services/codefixes/convertConstToLet.ts @@ -1,25 +1,26 @@ +import { tryCast } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isVariableDeclarationList } from "../../compiler/factory/nodeTests"; import { - addToSeen, - Diagnostics, - factory, - findChildOfKind, - getSymbolId, - getTokenAtPosition, - isVariableDeclarationList, Program, SourceFile, Symbol, SyntaxKind, - textChanges, Token, - tryCast, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { addToSeen, getSymbolId } from "../../compiler/utilities"; import { createCodeFixActionMaybeFixAll, createCombinedCodeActions, eachDiagnostic, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + findChildOfKind, + getTokenAtPosition, +} from "../utilities"; const fixId = "fixConvertConstToLet"; const errorCodes = [Diagnostics.Cannot_assign_to_0_because_it_is_a_constant.code]; @@ -31,14 +32,14 @@ registerCodeFix({ const info = getInfo(sourceFile, span.start, program); if (info === undefined) return; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info.token)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, info.token)); return [createCodeFixActionMaybeFixAll(fixId, changes, Diagnostics.Convert_const_to_let, fixId, Diagnostics.Convert_all_const_to_let)]; }, getAllCodeActions: context => { const { program } = context; const seen = new Map(); - return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => { + return createCombinedCodeActions(ChangeTracker.with(context, changes => { eachDiagnostic(context, errorCodes, diag => { const info = getInfo(diag.file, diag.start, program); if (info) { @@ -72,6 +73,6 @@ function getInfo(sourceFile: SourceFile, pos: number, program: Program): Info | return { symbol, token: constToken }; } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Token) { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, token: Token) { changes.replaceNode(sourceFile, token, factory.createToken(SyntaxKind.LetKeyword)); } diff --git a/src/services/codefixes/convertFunctionToEs6Class.ts b/src/services/codefixes/convertFunctionToEs6Class.ts index 5073d687a67ed..1fd317b373e27 100644 --- a/src/services/codefixes/convertFunctionToEs6Class.ts +++ b/src/services/codefixes/convertFunctionToEs6Class.ts @@ -1,78 +1,88 @@ import { - __String, - AccessExpression, - ArrowFunction, - BinaryExpression, - Block, - canHaveModifiers, - ClassDeclaration, - ClassElement, - CodeFixContext, - CompilerOptions, concatenate, - copyLeadingComments, - Diagnostics, every, - Expression, - factory, filter, forEach, - FunctionDeclaration, - FunctionExpression, - getEmitScriptTarget, - getNameOfDeclaration, - getQuotePreference, - getTokenAtPosition, - idText, - isAccessExpression, + some, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, isBinaryExpression, isFunctionDeclaration, isFunctionExpression, - isFunctionLike, - isGetOrSetAccessorDeclaration, isIdentifier, - isIdentifierText, isMethodDeclaration, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, - isSourceFileJS, - isStringLiteralLike, isVariableDeclaration, isVariableDeclarationList, +} from "../../compiler/factory/nodeTests"; +import { canHaveModifiers } from "../../compiler/factory/utilitiesPublic"; +import { isIdentifierText } from "../../compiler/scanner"; +import { + __String, + AccessExpression, + ArrowFunction, + BinaryExpression, + Block, + ClassDeclaration, + ClassElement, + CompilerOptions, + Expression, + FunctionDeclaration, + FunctionExpression, Modifier, Node, ObjectLiteralElementLike, ObjectLiteralExpression, PropertyAccessExpression, PropertyName, - QuotePreference, - some, SourceFile, Symbol, SymbolFlags, - symbolName, SyntaxKind, - textChanges, TypeChecker, UserPreferences, VariableDeclaration, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getEmitScriptTarget, + isAccessExpression, + isSourceFileJS, +} from "../../compiler/utilities"; +import { + getNameOfDeclaration, + idText, + isFunctionLike, + isGetOrSetAccessorDeclaration, + isStringLiteralLike, + symbolName, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { CodeFixContext } from "../types"; +import { + copyLeadingComments, + getQuotePreference, + getTokenAtPosition, + QuotePreference, +} from "../utilities"; const fixId = "convertFunctionToEs6Class"; const errorCodes = [Diagnostics.This_constructor_function_may_be_converted_to_a_class_declaration.code]; registerCodeFix({ errorCodes, getCodeActions(context: CodeFixContext) { - const changes = textChanges.ChangeTracker.with(context, t => + const changes = ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start, context.program.getTypeChecker(), context.preferences, context.program.getCompilerOptions())); return [createCodeFixAction(fixId, changes, Diagnostics.Convert_function_to_an_ES2015_class, fixId, Diagnostics.Convert_all_constructor_functions_to_classes)]; }, @@ -81,7 +91,7 @@ registerCodeFix({ doChange(changes, err.file, err.start, context.program.getTypeChecker(), context.preferences, context.program.getCompilerOptions())), }); -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, position: number, checker: TypeChecker, preferences: UserPreferences, compilerOptions: CompilerOptions): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, position: number, checker: TypeChecker, preferences: UserPreferences, compilerOptions: CompilerOptions): void { const ctorSymbol = checker.getSymbolAtLocation(getTokenAtPosition(sourceFile, position))!; if (!ctorSymbol || !ctorSymbol.valueDeclaration || !(ctorSymbol.flags & (SymbolFlags.Function | SymbolFlags.Variable))) { // Bad input diff --git a/src/services/codefixes/convertLiteralTypeToMappedType.ts b/src/services/codefixes/convertLiteralTypeToMappedType.ts index 1e68d28758d1c..c2c54f57c8f0a 100644 --- a/src/services/codefixes/convertLiteralTypeToMappedType.ts +++ b/src/services/codefixes/convertLiteralTypeToMappedType.ts @@ -1,21 +1,23 @@ +import { cast } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - cast, - Diagnostics, - factory, - getTokenAtPosition, isIdentifier, isPropertySignature, isTypeLiteralNode, +} from "../../compiler/factory/nodeTests"; +import { SourceFile, - textChanges, TypeLiteralNode, TypeNode, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "convertLiteralTypeToMappedType"; const errorCodes = [Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Did_you_mean_to_use_1_in_0.code]; @@ -29,7 +31,7 @@ registerCodeFix({ return undefined; } const { name, constraint } = info; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, info)); return [createCodeFixAction(fixId, changes, [Diagnostics.Convert_0_to_1_in_0, constraint, name], fixId, Diagnostics.Convert_all_type_literals_to_mapped_type)]; }, fixIds: [fixId], @@ -63,7 +65,7 @@ function getInfo(sourceFile: SourceFile, pos: number): Info | undefined { return undefined; } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { container, typeNode, constraint, name }: Info): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, { container, typeNode, constraint, name }: Info): void { changes.replaceNode(sourceFile, container, factory.createMappedTypeNode( /*readonlyToken*/ undefined, factory.createTypeParameterDeclaration(/*modifiers*/ undefined, name, factory.createTypeReferenceNode(constraint)), diff --git a/src/services/codefixes/convertToAsyncFunction.ts b/src/services/codefixes/convertToAsyncFunction.ts index 734f1b09ec44a..806f64d558f71 100644 --- a/src/services/codefixes/convertToAsyncFunction.ts +++ b/src/services/codefixes/convertToAsyncFunction.ts @@ -1,88 +1,100 @@ import { - ArrowFunction, - AwaitExpression, - BindingName, - BindingPattern, - Block, - CallExpression, - canBeConvertedToAsync, - canHaveSymbol, - CodeFixContext, concatenate, createMultiMap, - Debug, - Diagnostics, elementAt, emptyArray, every, - Expression, - factory, firstOrUndefined, flatMap, forEach, - forEachChild, - forEachReturnStatement, - FunctionExpression, - FunctionLikeDeclaration, - GeneratedIdentifierFlags, - getContainingFunction, - getNodeId, - getObjectFlags, - getOriginalNode, - getSymbolId, - getSynthesizedDeepClone, - getSynthesizedDeepCloneWithReplacements, - getTokenAtPosition, - hasPropertyAccessExpressionWithName, - Identifier, - idText, + lastOrUndefined, + returnTrue, + tryCast, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isBindingElement, isBlock, isCallExpression, - isExpression, - isFixablePromiseHandler, - isFunctionLike, - isFunctionLikeDeclaration, - isGeneratedIdentifier, isIdentifier, - isInJSFile, isObjectBindingPattern, isOmittedExpression, isParameter, isPropertyAccessExpression, isReturnStatement, - isReturnStatementWithFixablePromiseHandler, isVariableDeclaration, - lastOrUndefined, - moveRangePastModifiers, +} from "../../compiler/factory/nodeTests"; +import { forEachChild } from "../../compiler/parser"; +import { forEachReturnStatement } from "../../compiler/parserUtilities"; +import { skipTrivia } from "../../compiler/scanner"; +import { + ArrowFunction, + AwaitExpression, + BindingName, + BindingPattern, + Block, + CallExpression, + Expression, + FunctionExpression, + FunctionLikeDeclaration, + GeneratedIdentifierFlags, + Identifier, Node, NodeFlags, ObjectFlags, PropertyAccessExpression, - returnsPromise, ReturnStatement, - returnTrue, Signature, SignatureKind, - skipTrivia, SourceFile, Statement, Symbol, SyntaxKind, - textChanges, - tryCast, TryStatement, Type, TypeChecker, TypeNode, TypeReference, UnionReduction, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getContainingFunction, + getNodeId, + getObjectFlags, + getSymbolId, + isInJSFile, + moveRangePastModifiers, +} from "../../compiler/utilities"; +import { + canHaveSymbol, + getOriginalNode, + idText, + isExpression, + isFunctionLike, + isFunctionLikeDeclaration, + isGeneratedIdentifier, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { + canBeConvertedToAsync, + isFixablePromiseHandler, + isReturnStatementWithFixablePromiseHandler, + returnsPromise, +} from "../suggestionDiagnostics"; +import { ChangeTracker } from "../textChanges"; +import { CodeFixContext } from "../types"; +import { + getSynthesizedDeepClone, + getSynthesizedDeepCloneWithReplacements, + getTokenAtPosition, + hasPropertyAccessExpressionWithName, +} from "../utilities"; const fixId = "convertToAsyncFunction"; const errorCodes = [Diagnostics.This_may_be_converted_to_an_async_function.code]; @@ -91,7 +103,7 @@ registerCodeFix({ errorCodes, getCodeActions(context: CodeFixContext) { codeActionSucceeded = true; - const changes = textChanges.ChangeTracker.with(context, (t) => convertToAsyncFunction(t, context.sourceFile, context.span.start, context.program.getTypeChecker())); + const changes = ChangeTracker.with(context, (t) => convertToAsyncFunction(t, context.sourceFile, context.span.start, context.program.getTypeChecker())); return codeActionSucceeded ? [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_async_function, fixId, Diagnostics.Convert_all_to_async_functions)] : []; }, fixIds: [fixId], @@ -134,7 +146,7 @@ interface PromiseReturningCallExpression extends CallExpres }; } -function convertToAsyncFunction(changes: textChanges.ChangeTracker, sourceFile: SourceFile, position: number, checker: TypeChecker): void { +function convertToAsyncFunction(changes: ChangeTracker, sourceFile: SourceFile, position: number, checker: TypeChecker): void { // get the function declaration - returns a promise const tokenAtPosition = getTokenAtPosition(sourceFile, position); let functionToConvert: FunctionLikeDeclaration | undefined; diff --git a/src/services/codefixes/convertToEsModule.ts b/src/services/codefixes/convertToEsModule.ts index b15a0521c1cb1..3452f00d464a0 100644 --- a/src/services/codefixes/convertToEsModule.ts +++ b/src/services/codefixes/convertToEsModule.ts @@ -1,59 +1,51 @@ +import { isExportsOrModuleExportsOrAlias } from "../../compiler/binder"; import { - __String, arrayFrom, + concatenate, + createMultiMap, + emptyMap, + filter, + flatMap, + forEach, + isArray, + map, + mapAllOrFail, + mapIterator, + some, +} from "../../compiler/core"; +import { ReadonlyCollection } from "../../compiler/corePublic"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { + isArrowFunction, + isBinaryExpression, + isClassExpression, + isFunctionExpression, + isIdentifier, + isObjectLiteralExpression, + isPropertyAccessExpression, + isVariableStatement, +} from "../../compiler/factory/nodeTests"; +import { getModeForUsageLocation } from "../../compiler/program"; +import { + __String, ArrowFunction, BinaryExpression, BindingElement, BindingName, ClassDeclaration, ClassExpression, - concatenate, - copyEntries, - createMultiMap, - createRange, - Debug, - Diagnostics, - emptyMap, ExportDeclaration, ExportSpecifier, Expression, ExpressionStatement, - factory, - filter, - findChildOfKind, - flatMap, - forEach, FunctionDeclaration, FunctionExpression, - getEmitScriptTarget, - getModeForUsageLocation, - getQuotePreference, - getResolvedModule, - getSynthesizedDeepClone, - getSynthesizedDeepClones, - getSynthesizedDeepClonesWithReplacements, - getSynthesizedDeepCloneWithReplacements, Identifier, ImportDeclaration, - importFromModuleSpecifier, ImportSpecifier, InternalSymbolName, - isArray, - isArrowFunction, - isBinaryExpression, - isClassExpression, - isExportsOrModuleExportsOrAlias, - isFunctionExpression, - isIdentifier, - isIdentifierANonContextualKeyword, - isObjectLiteralExpression, - isPropertyAccessExpression, - isRequireCall, - isVariableStatement, - makeImport, - map, - mapAllOrFail, - mapIterator, MethodDeclaration, Modifier, Node, @@ -62,31 +54,47 @@ import { ObjectLiteralElementLike, ObjectLiteralExpression, PropertyAccessExpression, - QuotePreference, - rangeContainsRange, - ReadonlyCollection, ScriptTarget, - some, SourceFile, Statement, StringLiteralLike, SymbolFlags, SyntaxKind, - textChanges, TypeChecker, VariableStatement, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + copyEntries, + createRange, + getEmitScriptTarget, + getResolvedModule, + importFromModuleSpecifier, + isIdentifierANonContextualKeyword, + isRequireCall, +} from "../../compiler/utilities"; import { createCodeFixActionWithoutFixAll, - moduleSpecifierToValidIdentifier, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + findChildOfKind, + getQuotePreference, + getSynthesizedDeepClone, + getSynthesizedDeepClones, + getSynthesizedDeepClonesWithReplacements, + getSynthesizedDeepCloneWithReplacements, + makeImport, + QuotePreference, + rangeContainsRange, +} from "../utilities"; +import { moduleSpecifierToValidIdentifier } from "./importAdder"; registerCodeFix({ errorCodes: [Diagnostics.File_is_a_CommonJS_module_it_may_be_converted_to_an_ES_module.code], getCodeActions(context) { const { sourceFile, program, preferences } = context; - const changes = textChanges.ChangeTracker.with(context, changes => { + const changes = ChangeTracker.with(context, changes => { const moduleExportsChangedToDefault = convertFileToEsModule(sourceFile, program.getTypeChecker(), changes, getEmitScriptTarget(program.getCompilerOptions()), getQuotePreference(sourceFile, preferences)); if (moduleExportsChangedToDefault) { for (const importingFile of program.getSourceFiles()) { @@ -99,7 +107,7 @@ registerCodeFix({ }, }); -function fixImportOfModuleExports(importingFile: SourceFile, exportingFile: SourceFile, changes: textChanges.ChangeTracker, quotePreference: QuotePreference) { +function fixImportOfModuleExports(importingFile: SourceFile, exportingFile: SourceFile, changes: ChangeTracker, quotePreference: QuotePreference) { for (const moduleSpecifier of importingFile.imports) { const imported = getResolvedModule(importingFile, moduleSpecifier.text, getModeForUsageLocation(importingFile, moduleSpecifier)); if (!imported || imported.resolvedFileName !== exportingFile.fileName) { @@ -121,7 +129,7 @@ function fixImportOfModuleExports(importingFile: SourceFile, exportingFile: Sour } /** @returns Whether we converted a `module.exports =` to a default export. */ -function convertFileToEsModule(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, target: ScriptTarget, quotePreference: QuotePreference): ModuleExportsChanged { +function convertFileToEsModule(sourceFile: SourceFile, checker: TypeChecker, changes: ChangeTracker, target: ScriptTarget, quotePreference: QuotePreference): ModuleExportsChanged { const identifiers: Identifiers = { original: collectFreeIdentifiers(sourceFile), additional: new Set() }; const exports = collectExportRenames(sourceFile, checker, identifiers); convertExportsAccesses(sourceFile, exports, changes); @@ -171,7 +179,7 @@ function collectExportRenames(sourceFile: SourceFile, checker: TypeChecker, iden return res; } -function convertExportsAccesses(sourceFile: SourceFile, exports: ExportRenames, changes: textChanges.ChangeTracker): void { +function convertExportsAccesses(sourceFile: SourceFile, exports: ExportRenames, changes: ChangeTracker): void { forEachExportReference(sourceFile, (node, isAssignmentLhs) => { if (isAssignmentLhs) { return; @@ -198,7 +206,7 @@ function convertStatement( sourceFile: SourceFile, statement: Statement, checker: TypeChecker, - changes: textChanges.ChangeTracker, + changes: ChangeTracker, identifiers: Identifiers, target: ScriptTarget, exports: ExportRenames, @@ -234,7 +242,7 @@ function convertStatement( function convertVariableStatement( sourceFile: SourceFile, statement: VariableStatement, - changes: textChanges.ChangeTracker, + changes: ChangeTracker, checker: TypeChecker, identifiers: Identifiers, target: ScriptTarget, @@ -300,7 +308,7 @@ function convertAssignment( sourceFile: SourceFile, checker: TypeChecker, assignment: BinaryExpression, - changes: textChanges.ChangeTracker, + changes: ChangeTracker, exports: ExportRenames, useSitesToUnqualify: Map | undefined, ): ModuleExportsChanged { @@ -363,7 +371,7 @@ function tryChangeModuleExportsObject(object: ObjectLiteralExpression, useSitesT function convertNamedExport( sourceFile: SourceFile, assignment: BinaryExpression & { left: PropertyAccessExpression }, - changes: textChanges.ChangeTracker, + changes: ChangeTracker, exports: ExportRenames, ): void { // If "originalKeywordKind" was set, this is e.g. `exports. @@ -402,7 +410,7 @@ function reExportDefault(moduleSpecifier: string): ExportDeclaration { return makeExportDeclaration([factory.createExportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, "default")], moduleSpecifier); } -function convertExportsPropertyAssignment({ left, right, parent }: BinaryExpression & { left: PropertyAccessExpression }, sourceFile: SourceFile, changes: textChanges.ChangeTracker): void { +function convertExportsPropertyAssignment({ left, right, parent }: BinaryExpression & { left: PropertyAccessExpression }, sourceFile: SourceFile, changes: ChangeTracker): void { const name = left.name.text; if ((isFunctionExpression(right) || isArrowFunction(right) || isClassExpression(right)) && (!right.name || right.name.text === name)) { // `exports.f = function() {}` -> `export function f() {}` -- Replace `exports.f = ` with `export `, and insert the name after `function`. diff --git a/src/services/codefixes/convertToMappedObjectType.ts b/src/services/codefixes/convertToMappedObjectType.ts index 1a3b8498a49f5..978fcf4a67751 100644 --- a/src/services/codefixes/convertToMappedObjectType.ts +++ b/src/services/codefixes/convertToMappedObjectType.ts @@ -1,32 +1,38 @@ import { cast, - Diagnostics, emptyArray, - factory, first, - getAllSuperTypeNodes, - getTokenAtPosition, - hasEffectiveReadonlyModifier, - idText, - IndexSignatureDeclaration, - InterfaceDeclaration, + tryCast, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isIdentifier, isIndexSignatureDeclaration, isInterfaceDeclaration, isTypeAliasDeclaration, +} from "../../compiler/factory/nodeTests"; +import { + IndexSignatureDeclaration, + InterfaceDeclaration, SourceFile, SyntaxKind, - textChanges, - tryCast, TypeAliasDeclaration, TypeLiteralNode, TypeNode, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getAllSuperTypeNodes, + hasEffectiveReadonlyModifier, +} from "../../compiler/utilities"; +import { idText } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "fixConvertToMappedObjectType"; const errorCodes = [Diagnostics.An_index_signature_parameter_type_cannot_be_a_literal_type_or_generic_type_Consider_using_a_mapped_object_type_instead.code]; @@ -39,7 +45,7 @@ registerCodeFix({ const { sourceFile, span } = context; const info = getInfo(sourceFile, span.start); if (!info) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, info)); const name = idText(info.container.name); return [createCodeFixAction(fixId, changes, [Diagnostics.Convert_0_to_mapped_object_type, name], fixId, [Diagnostics.Convert_0_to_mapped_object_type, name])]; }, @@ -66,7 +72,7 @@ function createTypeAliasFromInterface(declaration: FixableDeclaration, type: Typ return factory.createTypeAliasDeclaration(declaration.modifiers, declaration.name, declaration.typeParameters, type); } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { indexSignature, container }: Info): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, { indexSignature, container }: Info): void { const members = isInterfaceDeclaration(container) ? container.members : (container.type as TypeLiteralNode).members; const otherMembers = members.filter(member => !isIndexSignatureDeclaration(member)); const parameter = first(indexSignature.parameters); diff --git a/src/services/codefixes/convertToTypeOnlyExport.ts b/src/services/codefixes/convertToTypeOnlyExport.ts index b0e67f43b4cbd..018a8961afa24 100644 --- a/src/services/codefixes/convertToTypeOnlyExport.ts +++ b/src/services/codefixes/convertToTypeOnlyExport.ts @@ -1,35 +1,42 @@ import { - addToSeen, - CodeFixContextBase, contains, - createTextSpanFromNode, - Diagnostics, - ExportSpecifier, - factory, filter, - findDiagnosticForNode, - getDiagnosticsWithinSpan, - getNodeId, - getTokenAtPosition, - isExportSpecifier, + tryCast, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isExportSpecifier } from "../../compiler/factory/nodeTests"; +import { + ExportSpecifier, SourceFile, SyntaxKind, - textChanges, TextSpan, - tryCast, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { addToSeen, getNodeId } from "../../compiler/utilities"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { + ChangeTracker, + LeadingTriviaOption, + TrailingTriviaOption, +} from "../textChanges"; +import { CodeFixContextBase } from "../types"; +import { + createTextSpanFromNode, + findDiagnosticForNode, + getDiagnosticsWithinSpan, + getTokenAtPosition, +} from "../utilities"; const errorCodes = [Diagnostics.Re_exporting_a_type_when_0_is_enabled_requires_using_export_type.code]; const fixId = "convertToTypeOnlyExport"; registerCodeFix({ errorCodes, getCodeActions: function getCodeActionsToConvertToTypeOnlyExport(context) { - const changes = textChanges.ChangeTracker.with(context, t => fixSingleExportDeclaration(t, getExportSpecifierForDiagnosticSpan(context.span, context.sourceFile), context)); + const changes = ChangeTracker.with(context, t => fixSingleExportDeclaration(t, getExportSpecifierForDiagnosticSpan(context.span, context.sourceFile), context)); if (changes.length) { return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_type_only_export, fixId, Diagnostics.Convert_all_re_exported_types_to_type_only_exports)]; } @@ -50,7 +57,7 @@ function getExportSpecifierForDiagnosticSpan(span: TextSpan, sourceFile: SourceF return tryCast(getTokenAtPosition(sourceFile, span.start).parent, isExportSpecifier); } -function fixSingleExportDeclaration(changes: textChanges.ChangeTracker, exportSpecifier: ExportSpecifier | undefined, context: CodeFixContextBase) { +function fixSingleExportDeclaration(changes: ChangeTracker, exportSpecifier: ExportSpecifier | undefined, context: CodeFixContextBase) { if (!exportSpecifier) { return; } @@ -79,8 +86,8 @@ function fixSingleExportDeclaration(changes: textChanges.ChangeTracker, exportSp ); changes.replaceNode(context.sourceFile, exportDeclaration, valueExportDeclaration, { - leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll, - trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude + leadingTriviaOption: LeadingTriviaOption.IncludeAll, + trailingTriviaOption: TrailingTriviaOption.Exclude, }); changes.insertNodeAfter(context.sourceFile, exportDeclaration, typeExportDeclaration); } diff --git a/src/services/codefixes/convertToTypeOnlyImport.ts b/src/services/codefixes/convertToTypeOnlyImport.ts index 5b553bb33d0e5..55a59f8943ca1 100644 --- a/src/services/codefixes/convertToTypeOnlyImport.ts +++ b/src/services/codefixes/convertToTypeOnlyImport.ts @@ -1,22 +1,19 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isImportDeclaration, isImportSpecifier } from "../../compiler/factory/nodeTests"; import { - Diagnostics, - factory, - getSynthesizedDeepClone, - getSynthesizedDeepClones, - getTokenAtPosition, ImportClause, ImportDeclaration, ImportSpecifier, - isImportDeclaration, - isImportSpecifier, SourceFile, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getSynthesizedDeepClone, getSynthesizedDeepClones, getTokenAtPosition } from "../utilities"; const errorCodes = [ Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_importsNotUsedAsValues_is_set_to_error.code, @@ -29,7 +26,7 @@ registerCodeFix({ getCodeActions: function getCodeActionsToConvertToTypeOnlyImport(context) { const declaration = getDeclaration(context.sourceFile, context.span.start); if (declaration) { - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, declaration)); + const changes = ChangeTracker.with(context, t => doChange(t, context.sourceFile, declaration)); return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_type_only_import, fixId, Diagnostics.Convert_all_imports_not_used_as_a_value_to_type_only_imports)]; } return undefined; @@ -50,7 +47,7 @@ function getDeclaration(sourceFile: SourceFile, pos: number) { return isImportSpecifier(parent) || isImportDeclaration(parent) && parent.importClause ? parent : undefined; } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ImportDeclaration | ImportSpecifier) { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, declaration: ImportDeclaration | ImportSpecifier) { if (isImportSpecifier(declaration)) { changes.replaceNode(sourceFile, declaration, factory.updateImportSpecifier(declaration, /*isTypeOnly*/ true, declaration.propertyName, declaration.name)); } diff --git a/src/services/codefixes/convertTypedefToType.ts b/src/services/codefixes/convertTypedefToType.ts index 5cf04b1aaa82f..892dc40220f99 100644 --- a/src/services/codefixes/convertTypedefToType.ts +++ b/src/services/codefixes/convertTypedefToType.ts @@ -1,27 +1,35 @@ import { - Diagnostics, - factory, - forEach, - getSynthesizedDeepClone, - getTokenAtPosition, - hasJSDocNodes, - InterfaceDeclaration, + mapDefined, + some, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isJSDocTypedefTag, isJSDocTypeLiteral, +} from "../../compiler/factory/nodeTests"; +import { + InterfaceDeclaration, JSDocPropertyLikeTag, JSDocTypedefTag, JSDocTypeExpression, JSDocTypeLiteral, - mapDefined, Node, PropertySignature, - some, SourceFile, SyntaxKind, - textChanges, TypeAliasDeclaration, -} from "../_namespaces/ts"; -import { codeFixAll, createCodeFixAction, registerCodeFix } from "../_namespaces/ts.codefix"; +} from "../../compiler/types"; +import { + codeFixAll, + createCodeFixAction, + registerCodeFix, +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + getSynthesizedDeepClone, + getTokenAtPosition, +} from "../utilities"; const fixId = "convertTypedefToType"; const errorCodes = [Diagnostics.JSDoc_typedef_may_be_converted_to_TypeScript_type.code]; @@ -34,7 +42,7 @@ registerCodeFix({ context.span.start ); if (!node) return; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, node, context.sourceFile)); + const changes = ChangeTracker.with(context, t => doChange(t, node, context.sourceFile)); if (changes.length > 0) { return [ @@ -54,14 +62,14 @@ registerCodeFix({ }) }); -function doChange(changes: textChanges.ChangeTracker, node: Node, sourceFile: SourceFile) { +function doChange(changes: ChangeTracker, node: Node, sourceFile: SourceFile) { if (isJSDocTypedefTag(node)) { fixSingleTypeDef(changes, node, sourceFile); } } function fixSingleTypeDef( - changes: textChanges.ChangeTracker, + changes: ChangeTracker, typeDefNode: JSDocTypedefTag | undefined, sourceFile: SourceFile, ) { @@ -168,11 +176,3 @@ function createSignatureFromTypeLiteral(typeLiteral: JSDocTypeLiteral): Property function getPropertyName(tag: JSDocPropertyLikeTag): string | undefined { return tag.name.kind === SyntaxKind.Identifier ? tag.name.text : tag.name.right.text; } - -/** @internal */ -export function getJSDocTypedefNode(node: Node): JSDocTypedefTag | undefined { - if (hasJSDocNodes(node)) { - return forEach(node.jsDoc, (node) => node.tags?.find(isJSDocTypedefTag)); - } - return undefined; -} diff --git a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts index 466a8a9bcd719..7ad41677926aa 100644 --- a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts +++ b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts @@ -1,21 +1,23 @@ +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - Debug, - Diagnostics, - factory, - findAncestor, - getTokenAtPosition, - Identifier, isIdentifier, isQualifiedName, +} from "../../compiler/factory/nodeTests"; +import { + Identifier, QualifiedName, SourceFile, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { findAncestor } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "correctQualifiedNameToIndexedAccessType"; const errorCodes = [Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1.code]; @@ -24,7 +26,7 @@ registerCodeFix({ getCodeActions(context) { const qualifiedName = getQualifiedName(context.sourceFile, context.span.start); if (!qualifiedName) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, qualifiedName)); + const changes = ChangeTracker.with(context, t => doChange(t, context.sourceFile, qualifiedName)); const newText = `${qualifiedName.left.text}["${qualifiedName.right.text}"]`; return [createCodeFixAction(fixId, changes, [Diagnostics.Rewrite_as_the_indexed_access_type_0, newText], fixId, Diagnostics.Rewrite_all_as_indexed_access_types)]; }, @@ -43,7 +45,7 @@ function getQualifiedName(sourceFile: SourceFile, pos: number): QualifiedName & return isIdentifier(qualifiedName.left) ? qualifiedName as QualifiedName & { left: Identifier } : undefined; } -function doChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, qualifiedName: QualifiedName): void { +function doChange(changeTracker: ChangeTracker, sourceFile: SourceFile, qualifiedName: QualifiedName): void { const rightText = qualifiedName.right.text; const replacement = factory.createIndexedAccessTypeNode( factory.createTypeReferenceNode(qualifiedName.left, /*typeArguments*/ undefined), diff --git a/src/services/codefixes/disableJsDiagnostics.ts b/src/services/codefixes/disableJsDiagnostics.ts index 96202931ae090..f189fb4b31480 100644 --- a/src/services/codefixes/disableJsDiagnostics.ts +++ b/src/services/codefixes/disableJsDiagnostics.ts @@ -1,26 +1,37 @@ import { - CodeFixAction, - createTextChange, - createTextSpan, - createTextSpanFromBounds, + mapDefined, + tryAddToSet, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { getLineAndCharacterOfPosition } from "../../compiler/scanner"; +import { DiagnosticCategory, - Diagnostics, - getLineAndCharacterOfPosition, - getNewLineOrDefaultFromHost, + SourceFile, +} from "../../compiler/types"; +import { isCheckJsEnabledForFile, isInJSFile, - mapDefined, - SourceFile, - textChanges, - tryAddToSet, -} from "../_namespaces/ts"; +} from "../../compiler/utilities"; +import { + createTextSpan, + createTextSpanFromBounds, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, createCodeFixActionWithoutFixAll, createFileTextChanges, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { + ChangeTracker, + isValidLocationToAddComment, +} from "../textChanges"; +import { CodeFixAction } from "../types"; +import { + createTextChange, + getNewLineOrDefaultFromHost, +} from "../utilities"; const fixName = "disableJsDiagnostics"; const fixId = "disableJsDiagnostics"; @@ -51,8 +62,8 @@ registerCodeFix({ Diagnostics.Disable_checking_for_this_file), ]; - if (textChanges.isValidLocationToAddComment(sourceFile, span.start)) { - fixes.unshift(createCodeFixAction(fixName, textChanges.ChangeTracker.with(context, t => makeChange(t, sourceFile, span.start)), Diagnostics.Ignore_this_error_message, fixId, Diagnostics.Add_ts_ignore_to_all_error_messages)); + if (isValidLocationToAddComment(sourceFile, span.start)) { + fixes.unshift(createCodeFixAction(fixName, ChangeTracker.with(context, t => makeChange(t, sourceFile, span.start)), Diagnostics.Ignore_this_error_message, fixId, Diagnostics.Add_ts_ignore_to_all_error_messages)); } return fixes; @@ -61,14 +72,14 @@ registerCodeFix({ getAllCodeActions: context => { const seenLines = new Set(); return codeFixAll(context, errorCodes, (changes, diag) => { - if (textChanges.isValidLocationToAddComment(diag.file, diag.start)) { + if (isValidLocationToAddComment(diag.file, diag.start)) { makeChange(changes, diag.file, diag.start, seenLines); } }); }, }); -function makeChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, position: number, seenLines?: Set) { +function makeChange(changes: ChangeTracker, sourceFile: SourceFile, position: number, seenLines?: Set) { const { line: lineNumber } = getLineAndCharacterOfPosition(sourceFile, position); // Only need to add `// @ts-ignore` for a line once. if (!seenLines || tryAddToSet(seenLines, lineNumber)) { diff --git a/src/services/codefixes/fixAddMissingConstraint.ts b/src/services/codefixes/fixAddMissingConstraint.ts index c5b2bd3573789..05947b9aeaaa4 100644 --- a/src/services/codefixes/fixAddMissingConstraint.ts +++ b/src/services/codefixes/fixAddMissingConstraint.ts @@ -1,41 +1,51 @@ import { - addToSeen, - createTextSpan, - DiagnosticMessageChain, - Diagnostics, - factory, find, - flattenDiagnosticMessageText, - getEmitScriptTarget, - getNodeId, - getTokenAtPosition, - isExpression, + isString, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isIdentifier, isMappedTypeNode, - isString, - isTypeNode, isTypeParameterDeclaration, - LanguageServiceHost, +} from "../../compiler/factory/nodeTests"; +import { flattenDiagnosticMessageText } from "../../compiler/program"; +import { + DiagnosticMessageChain, Node, Program, SourceFile, - textChanges, TextSpan, Type, TypeChecker, TypeParameterDeclaration, UserPreferences, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + addToSeen, + getEmitScriptTarget, + getNodeId, +} from "../../compiler/utilities"; +import { + createTextSpan, + isExpression, + isTypeNode, +} from "../../compiler/utilitiesPublic"; import { createCodeFixAction, createCombinedCodeActions, - createImportAdder, eachDiagnostic, + registerCodeFix, +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { LanguageServiceHost } from "../types"; +import { getTokenAtPosition } from "../utilities"; +import { findAncestorMatchingSpan, getNoopSymbolTrackerWithResolver, - registerCodeFix, typeToAutoImportableTypeNode, -} from "../_namespaces/ts.codefix"; +} from "./helpers"; +import { createImportAdder } from "./importAdder"; const fixId = "addMissingConstraint"; const errorCodes = [ @@ -57,7 +67,7 @@ registerCodeFix({ const info = getInfo(program, sourceFile, span); if (info === undefined) return; - const changes = textChanges.ChangeTracker.with(context, t => addMissingConstraint(t, program, preferences, host, sourceFile, info)); + const changes = ChangeTracker.with(context, t => addMissingConstraint(t, program, preferences, host, sourceFile, info)); return [createCodeFixAction(fixId, changes, Diagnostics.Add_extends_constraint, fixId, Diagnostics.Add_extends_constraint_to_all_type_parameters)]; }, fixIds: [fixId], @@ -65,7 +75,7 @@ registerCodeFix({ const { program, preferences, host } = context; const seen = new Map(); - return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => { + return createCombinedCodeActions(ChangeTracker.with(context, changes => { eachDiagnostic(context, errorCodes, diag => { const info = getInfo(program, diag.file, createTextSpan(diag.start, diag.length)); if (info) { @@ -112,7 +122,7 @@ function getInfo(program: Program, sourceFile: SourceFile, span: TextSpan): Info return undefined; } -function addMissingConstraint(changes: textChanges.ChangeTracker, program: Program, preferences: UserPreferences, host: LanguageServiceHost, sourceFile: SourceFile, info: Info): void { +function addMissingConstraint(changes: ChangeTracker, program: Program, preferences: UserPreferences, host: LanguageServiceHost, sourceFile: SourceFile, info: Info): void { const { declaration, constraint } = info; const checker = program.getTypeChecker(); @@ -122,7 +132,7 @@ function addMissingConstraint(changes: textChanges.ChangeTracker, program: Progr else { const scriptTarget = getEmitScriptTarget(program.getCompilerOptions()); const tracker = getNoopSymbolTrackerWithResolver({ program, host }); - const importAdder = createImportAdder(sourceFile, program, preferences, host); + const importAdder = createImportAdder(sourceFile, program, preferences, host, /*useAutoImportProvider*/ false); const typeNode = typeToAutoImportableTypeNode(checker, importAdder, constraint, /*contextNode*/ undefined, scriptTarget, /*flags*/ undefined, tracker); if (typeNode) { changes.replaceNode(sourceFile, declaration, factory.updateTypeParameterDeclaration(declaration, /*modifiers*/ undefined, declaration.name, typeNode, declaration.default)); diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 2231cd0509a9b..9ea3d300b8f4c 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -1,61 +1,36 @@ import { - __String, - addToSeen, arrayFrom, - BigIntLiteralType, - BinaryExpression, - CallExpression, - CheckFlags, - ClassLikeDeclaration, - CodeFixAction, - CodeFixContext, - CodeFixContextBase, concatenate, - createPropertyNameNodeForIdentifierOrLiteral, - Debug, - Diagnostics, emptyArray, - EnumDeclaration, - Expression, - factory, filter, find, - findAncestor, findIndex, firstDefined, firstOrUndefined, firstOrUndefinedIterator, - FunctionExpression, - getCheckFlags, - getClassLikeDeclarationOfSymbol, - getEmitScriptTarget, - getEscapedTextOfJsxAttributeName, - getFirstConstructorWithBody, - getNodeId, - getObjectFlags, getOrUpdate, - getQuotePreference, - getSourceFileOfNode, - getTokenAtPosition, - hasAbstractModifier, - hasInitializer, - Identifier, - idText, - InterfaceDeclaration, + length, + map, + or, + singleElementArray, + singleOrUndefined, + some, + tryCast, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isCallExpression, - isClassLike, isComputedPropertyName, isConstructorDeclaration, isEnumDeclaration, isFunctionTypeNode, isIdentifier, - isIdentifierText, isInterfaceDeclaration, isJsxAttribute, isJsxExpression, - isJsxOpeningLikeElement, isJsxSpreadAttribute, - isMemberName, isMethodDeclaration, isMethodSignature, isModuleDeclaration, @@ -66,14 +41,25 @@ import { isPropertyDeclaration, isReturnStatement, isSourceFile, - isSourceFileFromLibrary, - isSourceFileJS, - isTransientSymbol, isTypeLiteralNode, +} from "../../compiler/factory/nodeTests"; +import { createPropertyNameNodeForIdentifierOrLiteral } from "../../compiler/factory/utilities"; +import { setParent } from "../../compiler/parserUtilities"; +import { isIdentifierText } from "../../compiler/scanner"; +import { + __String, + BigIntLiteralType, + BinaryExpression, + CallExpression, + CheckFlags, + ClassLikeDeclaration, + EnumDeclaration, + Expression, + FunctionExpression, + Identifier, + InterfaceDeclaration, JsxOpeningLikeElement, LanguageVariant, - length, - map, MethodDeclaration, ModifierFlags, ModuleDeclaration, @@ -82,28 +68,18 @@ import { NumberLiteralType, ObjectFlags, ObjectLiteralExpression, - or, PrivateIdentifier, Program, PropertyDeclaration, - QuotePreference, ReturnStatement, ScriptTarget, - setParent, Signature, SignatureKind, - singleElementArray, - singleOrUndefined, - skipConstraint, - some, SourceFile, - startsWithUnderscore, StringLiteralType, Symbol, SymbolFlags, SyntaxKind, - textChanges, - tryCast, Type, TypeChecker, TypeFlags, @@ -111,20 +87,64 @@ import { TypeNode, TypeReference, UnionType, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + addToSeen, + getCheckFlags, + getClassLikeDeclarationOfSymbol, + getEmitScriptTarget, + getEscapedTextOfJsxAttributeName, + getFirstConstructorWithBody, + getNodeId, + getObjectFlags, + getSourceFileOfNode, + hasAbstractModifier, + isSourceFileJS, + isTransientSymbol, +} from "../../compiler/utilities"; +import { + findAncestor, + hasInitializer, + idText, + isClassLike, + isJsxOpeningLikeElement, + isMemberName, +} from "../../compiler/utilitiesPublic"; import { createCodeFixAction, createCodeFixActionWithoutFixAll, createCombinedCodeActions, - createImportAdder, + eachDiagnostic, + registerCodeFix, +} from "../codeFixProvider"; +import { + ChangeTracker, + LeadingTriviaOption, + TrailingTriviaOption, +} from "../textChanges"; +import { + CodeFixAction, + CodeFixContext, + CodeFixContextBase, +} from "../types"; +import { + getQuotePreference, + getTokenAtPosition, + isSourceFileFromLibrary, + QuotePreference, + skipConstraint, + startsWithUnderscore, +} from "../utilities"; +import { getAllSupers } from "./generateAccessors"; +import { createSignatureDeclarationFromCallExpression, createSignatureDeclarationFromSignature, createStubbedBody, - eachDiagnostic, - getAllSupers, +} from "./helpers"; +import { + createImportAdder, ImportAdder, - registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "./importAdder"; const fixMissingMember = "fixMissingMember"; const fixMissingProperties = "fixMissingProperties"; @@ -159,19 +179,19 @@ registerCodeFix({ return undefined; } if (info.kind === InfoKind.ObjectLiteral) { - const changes = textChanges.ChangeTracker.with(context, t => addObjectLiteralProperties(t, context, info)); + const changes = ChangeTracker.with(context, t => addObjectLiteralProperties(t, context, info)); return [createCodeFixAction(fixMissingProperties, changes, Diagnostics.Add_missing_properties, fixMissingProperties, Diagnostics.Add_all_missing_properties)]; } if (info.kind === InfoKind.JsxAttributes) { - const changes = textChanges.ChangeTracker.with(context, t => addJsxAttributes(t, context, info)); + const changes = ChangeTracker.with(context, t => addJsxAttributes(t, context, info)); return [createCodeFixAction(fixMissingAttributes, changes, Diagnostics.Add_missing_attributes, fixMissingAttributes, Diagnostics.Add_all_missing_attributes)]; } if (info.kind === InfoKind.Function || info.kind === InfoKind.Signature) { - const changes = textChanges.ChangeTracker.with(context, t => addFunctionDeclaration(t, context, info)); + const changes = ChangeTracker.with(context, t => addFunctionDeclaration(t, context, info)); return [createCodeFixAction(fixMissingFunctionDeclaration, changes, [Diagnostics.Add_missing_function_declaration_0, info.token.text], fixMissingFunctionDeclaration, Diagnostics.Add_all_missing_function_declarations)]; } if (info.kind === InfoKind.Enum) { - const changes = textChanges.ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), info)); + const changes = ChangeTracker.with(context, t => addEnumMemberDeclaration(t, context.program.getTypeChecker(), info)); return [createCodeFixAction(fixMissingMember, changes, [Diagnostics.Add_missing_enum_member_0, info.token.text], fixMissingMember, Diagnostics.Add_all_missing_members)]; } return concatenate(getActionsForMissingMethodDeclaration(context, info), getActionsForMissingMemberDeclaration(context, info)); @@ -183,7 +203,7 @@ registerCodeFix({ const seen = new Map(); const typeDeclToMembers = new Map(); - return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => { + return createCombinedCodeActions(ChangeTracker.with(context, changes => { eachDiagnostic(context, errorCodes, diag => { const info = getInfo(diag.file, diag.start, diag.code, checker, context.program); if (!info || !addToSeen(seen, getNodeId(info.parentDeclaration) + "#" + info.token.text)) { @@ -401,7 +421,7 @@ function createActionForAddMissingMemberInJavascriptFile(context: CodeFixContext return undefined; } - const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, declSourceFile, parentDeclaration, token, !!(modifierFlags & ModifierFlags.Static))); + const changes = ChangeTracker.with(context, t => addMissingMemberInJs(t, declSourceFile, parentDeclaration, token, !!(modifierFlags & ModifierFlags.Static))); if (changes.length === 0) { return undefined; } @@ -412,7 +432,7 @@ function createActionForAddMissingMemberInJavascriptFile(context: CodeFixContext return createCodeFixAction(fixMissingMember, changes, [diagnostic, token.text], fixMissingMember, Diagnostics.Add_all_missing_members); } -function addMissingMemberInJs(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier | PrivateIdentifier, makeStatic: boolean): void { +function addMissingMemberInJs(changeTracker: ChangeTracker, sourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier | PrivateIdentifier, makeStatic: boolean): void { const tokenName = token.text; if (makeStatic) { if (classDeclaration.kind === SyntaxKind.ClassExpression) { @@ -456,7 +476,7 @@ function createActionsForAddMissingMemberInTypeScriptFile(context: CodeFixContex const memberName = token.text; const isStatic = modifierFlags & ModifierFlags.Static; const typeNode = getTypeNode(context.program.getTypeChecker(), parentDeclaration, token); - const addPropertyDeclarationChanges = (modifierFlags: ModifierFlags) => textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, declSourceFile, parentDeclaration, memberName, typeNode, modifierFlags)); + const addPropertyDeclarationChanges = (modifierFlags: ModifierFlags) => ChangeTracker.with(context, t => addPropertyDeclaration(t, declSourceFile, parentDeclaration, memberName, typeNode, modifierFlags)); const actions = [createCodeFixAction(fixMissingMember, addPropertyDeclarationChanges(modifierFlags & ModifierFlags.Static), [isStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0, memberName], fixMissingMember, Diagnostics.Add_all_missing_members)]; if (isStatic || isPrivateIdentifier(token)) { @@ -486,7 +506,7 @@ function getTypeNode(checker: TypeChecker, node: ClassLikeDeclaration | Interfac return typeNode || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } -function addPropertyDeclaration(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, node: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, tokenName: string, typeNode: TypeNode, modifierFlags: ModifierFlags): void { +function addPropertyDeclaration(changeTracker: ChangeTracker, sourceFile: SourceFile, node: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, tokenName: string, typeNode: TypeNode, modifierFlags: ModifierFlags): void { const modifiers = modifierFlags ? factory.createNodeArray(factory.createModifiersFromModifierFlags(modifierFlags)) : undefined; const property = isClassLike(node) @@ -527,7 +547,7 @@ function createAddIndexSignatureAction(context: CodeFixContext, sourceFile: Sour [indexingParameter], typeNode); - const changes = textChanges.ChangeTracker.with(context, t => t.insertMemberAtStart(sourceFile, node, indexSignature)); + const changes = ChangeTracker.with(context, t => t.insertMemberAtStart(sourceFile, node, indexSignature)); // No fixId here because code-fix-all currently only works on adding individual named properties. return createCodeFixActionWithoutFixAll(fixMissingMember, changes, [Diagnostics.Add_index_signature_for_property_0, tokenName]); } @@ -544,7 +564,7 @@ function getActionsForMissingMethodDeclaration(context: CodeFixContext, info: Ty } const methodName = token.text; - const addMethodDeclarationChanges = (modifierFlags: ModifierFlags) => textChanges.ChangeTracker.with(context, t => addMethodDeclaration(context, t, call, token, modifierFlags, parentDeclaration, declSourceFile)); + const addMethodDeclarationChanges = (modifierFlags: ModifierFlags) => ChangeTracker.with(context, t => addMethodDeclaration(context, t, call, token, modifierFlags, parentDeclaration, declSourceFile)); const actions = [createCodeFixAction(fixMissingMember, addMethodDeclarationChanges(modifierFlags & ModifierFlags.Static), [modifierFlags & ModifierFlags.Static ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0, methodName], fixMissingMember, Diagnostics.Add_all_missing_members)]; if (modifierFlags & ModifierFlags.Private) { actions.unshift(createCodeFixActionWithoutFixAll(fixMissingMember, addMethodDeclarationChanges(ModifierFlags.Private), [Diagnostics.Declare_private_method_0, methodName])); @@ -554,14 +574,14 @@ function getActionsForMissingMethodDeclaration(context: CodeFixContext, info: Ty function addMethodDeclaration( context: CodeFixContextBase, - changes: textChanges.ChangeTracker, + changes: ChangeTracker, callExpression: CallExpression, name: Identifier, modifierFlags: ModifierFlags, parentDeclaration: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, sourceFile: SourceFile, ): void { - const importAdder = createImportAdder(sourceFile, context.program, context.preferences, context.host); + const importAdder = createImportAdder(sourceFile, context.program, context.preferences, context.host, /*useAutoImportProvider*/ false); const kind = isClassLike(parentDeclaration) ? SyntaxKind.MethodDeclaration : SyntaxKind.MethodSignature; const signatureDeclaration = createSignatureDeclarationFromCallExpression(kind, context, importAdder, callExpression, name, modifierFlags, parentDeclaration) as MethodDeclaration; const containingMethodDeclaration = tryGetContainingMethodDeclaration(parentDeclaration, callExpression); @@ -574,7 +594,7 @@ function addMethodDeclaration( importAdder.writeFixes(changes); } -function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: TypeChecker, { token, parentDeclaration }: EnumInfo) { +function addEnumMemberDeclaration(changes: ChangeTracker, checker: TypeChecker, { token, parentDeclaration }: EnumInfo) { /** * create initializer only literal enum that has string initializer. * value of initializer is a string literal that equal to name of enum member. @@ -592,14 +612,14 @@ function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: T parentDeclaration.name, concatenate(parentDeclaration.members, singleElementArray(enumMember)) ), { - leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll, - trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude + leadingTriviaOption: LeadingTriviaOption.IncludeAll, + trailingTriviaOption: TrailingTriviaOption.Exclude, }); } -function addFunctionDeclaration(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: FunctionInfo | SignatureInfo) { +function addFunctionDeclaration(changes: ChangeTracker, context: CodeFixContextBase, info: FunctionInfo | SignatureInfo) { const quotePreference = getQuotePreference(context.sourceFile, context.preferences); - const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host); + const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host, /*useAutoImportProvider*/ false); const functionDeclaration = info.kind === InfoKind.Function ? createSignatureDeclarationFromCallExpression(SyntaxKind.FunctionDeclaration, context, importAdder, info.call, idText(info.token), info.modifierFlags, info.parentDeclaration) : createSignatureDeclarationFromSignature(SyntaxKind.FunctionDeclaration, context, quotePreference, info.signature, createStubbedBody(Diagnostics.Function_not_implemented.message, quotePreference), info.token, /*modifiers*/ undefined, /*optional*/ undefined, /*enclosingDeclaration*/ undefined, importAdder); @@ -613,8 +633,8 @@ function addFunctionDeclaration(changes: textChanges.ChangeTracker, context: Cod importAdder.writeFixes(changes); } -function addJsxAttributes(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: JsxAttributesInfo) { - const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host); +function addJsxAttributes(changes: ChangeTracker, context: CodeFixContextBase, info: JsxAttributesInfo) { + const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host, /*useAutoImportProvider*/ false); const quotePreference = getQuotePreference(context.sourceFile, context.preferences); const checker = context.program.getTypeChecker(); const jsxAttributesNode = info.parentDeclaration.attributes; @@ -633,8 +653,8 @@ function addJsxAttributes(changes: textChanges.ChangeTracker, context: CodeFixCo importAdder.writeFixes(changes); } -function addObjectLiteralProperties(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: ObjectLiteralInfo) { - const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host); +function addObjectLiteralProperties(changes: ChangeTracker, context: CodeFixContextBase, info: ObjectLiteralInfo) { + const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host, /*useAutoImportProvider*/ false); const quotePreference = getQuotePreference(context.sourceFile, context.preferences); const target = getEmitScriptTarget(context.program.getCompilerOptions()); const checker = context.program.getTypeChecker(); @@ -643,8 +663,8 @@ function addObjectLiteralProperties(changes: textChanges.ChangeTracker, context: return factory.createPropertyAssignment(createPropertyNameFromSymbol(prop, target, quotePreference, checker), initializer); }); const options = { - leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, - trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude, + leadingTriviaOption: LeadingTriviaOption.Exclude, + trailingTriviaOption: TrailingTriviaOption.Exclude, indentation: info.indentation }; changes.replaceNode(context.sourceFile, info.parentDeclaration, factory.createObjectLiteralExpression([...info.parentDeclaration.properties, ...props], /*multiLine*/ true), options); @@ -769,7 +789,7 @@ function createPropertyNameFromSymbol(symbol: Symbol, target: ScriptTarget, quot const prop = checker.symbolToNode(symbol, SymbolFlags.Value, /*enclosingDeclaration*/ undefined, NodeBuilderFlags.WriteComputedProps); if (prop && isComputedPropertyName(prop)) return prop; } - return createPropertyNameNodeForIdentifierOrLiteral(symbol.name, target, quotePreference === QuotePreference.Single); + return createPropertyNameNodeForIdentifierOrLiteral(factory, symbol.name, target, quotePreference === QuotePreference.Single); } function findScope(node: Node) { diff --git a/src/services/codefixes/fixAddMissingNewOperator.ts b/src/services/codefixes/fixAddMissingNewOperator.ts index 32b17b39233d8..61f13488f23db 100644 --- a/src/services/codefixes/fixAddMissingNewOperator.ts +++ b/src/services/codefixes/fixAddMissingNewOperator.ts @@ -1,20 +1,20 @@ +import { cast } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isCallExpression } from "../../compiler/factory/nodeTests"; import { - cast, - Diagnostics, - factory, - getTokenAtPosition, - isCallExpression, Node, SourceFile, - textChanges, TextSpan, - textSpanEnd, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { textSpanEnd } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "addMissingNewOperator"; const errorCodes = [Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new.code]; @@ -22,7 +22,7 @@ registerCodeFix({ errorCodes, getCodeActions(context) { const { sourceFile, span } = context; - const changes = textChanges.ChangeTracker.with(context, t => addMissingNewOperator(t, sourceFile, span)); + const changes = ChangeTracker.with(context, t => addMissingNewOperator(t, sourceFile, span)); return [createCodeFixAction(fixId, changes, Diagnostics.Add_missing_new_operator_to_call, fixId, Diagnostics.Add_missing_new_operator_to_all_calls)]; }, fixIds: [fixId], @@ -30,7 +30,7 @@ registerCodeFix({ addMissingNewOperator(changes, context.sourceFile, diag)), }); -function addMissingNewOperator(changes: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan): void { +function addMissingNewOperator(changes: ChangeTracker, sourceFile: SourceFile, span: TextSpan): void { const call = cast(findAncestorMatchingSpan(sourceFile, span), isCallExpression); const newExpression = factory.createNewExpression(call.expression, call.typeArguments, call.arguments); diff --git a/src/services/codefixes/fixAddModuleReferTypeMissingTypeof.ts b/src/services/codefixes/fixAddModuleReferTypeMissingTypeof.ts index 20e76fbb1bf23..b63f45e83e6c1 100644 --- a/src/services/codefixes/fixAddModuleReferTypeMissingTypeof.ts +++ b/src/services/codefixes/fixAddModuleReferTypeMissingTypeof.ts @@ -1,18 +1,18 @@ +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - Debug, - Diagnostics, - factory, - getTokenAtPosition, ImportTypeNode, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixIdAddMissingTypeof = "fixAddModuleReferTypeMissingTypeof"; const fixId = fixIdAddMissingTypeof; @@ -23,7 +23,7 @@ registerCodeFix({ getCodeActions: function getCodeActionsToAddMissingTypeof(context) { const { sourceFile, span } = context; const importType = getImportTypeNode(sourceFile, span.start); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, importType)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, importType)); return [createCodeFixAction(fixId, changes, Diagnostics.Add_missing_typeof, fixId, Diagnostics.Add_missing_typeof)]; }, fixIds: [fixId], @@ -38,7 +38,7 @@ function getImportTypeNode(sourceFile: SourceFile, pos: number): ImportTypeNode return token.parent as ImportTypeNode; } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, importType: ImportTypeNode) { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, importType: ImportTypeNode) { const newTypeNode = factory.updateImportTypeNode(importType, importType.argument, importType.assertions, importType.qualifier, importType.typeArguments, /*isTypeOf*/ true); changes.replaceNode(sourceFile, importType, newTypeNode); } diff --git a/src/services/codefixes/fixAddVoidToPromise.ts b/src/services/codefixes/fixAddVoidToPromise.ts index 4abb3cdedda2d..424a2bfac4bd8 100644 --- a/src/services/codefixes/fixAddVoidToPromise.ts +++ b/src/services/codefixes/fixAddVoidToPromise.ts @@ -1,35 +1,39 @@ +import { some } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - CodeFixAllContext, - Diagnostics, - factory, - getJSDocTypeTag, - getTokenAtPosition, - idText, isCallExpression, isIdentifier, - isInJSFile, isNewExpression, isParameter, isParenthesizedExpression, isParenthesizedTypeNode, isTypeReferenceNode, isUnionTypeNode, +} from "../../compiler/factory/nodeTests"; +import { skipTrivia } from "../../compiler/scanner"; +import { NewExpression, ParameterDeclaration, Program, - skipTrivia, - some, SourceFile, SyntaxKind, - textChanges, TextSpan, TypeFlags, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { isInJSFile } from "../../compiler/utilities"; +import { + getJSDocTypeTag, + idText, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { CodeFixAllContext } from "../types"; +import { getTokenAtPosition } from "../utilities"; const fixName = "addVoidToPromise"; const fixId = "addVoidToPromise"; @@ -41,7 +45,7 @@ registerCodeFix({ errorCodes, fixIds: [fixId], getCodeActions(context) { - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span, context.program)); + const changes = ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span, context.program)); if (changes.length > 0) { return [createCodeFixAction(fixName, changes, Diagnostics.Add_void_to_Promise_resolved_without_a_value, fixId, Diagnostics.Add_void_to_all_Promises_resolved_without_a_value)]; } @@ -51,7 +55,7 @@ registerCodeFix({ } }); -function makeChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan, program: Program, seen?: Set) { +function makeChange(changes: ChangeTracker, sourceFile: SourceFile, span: TextSpan, program: Program, seen?: Set) { const node = getTokenAtPosition(sourceFile, span.start); if (!isIdentifier(node) || !isCallExpression(node.parent) || node.parent.expression !== node || node.parent.arguments.length !== 0) return; diff --git a/src/services/codefixes/fixAwaitInSyncFunction.ts b/src/services/codefixes/fixAwaitInSyncFunction.ts index f12ba12e3f0fc..9e7ca896d0d2a 100644 --- a/src/services/codefixes/fixAwaitInSyncFunction.ts +++ b/src/services/codefixes/fixAwaitInSyncFunction.ts @@ -1,30 +1,36 @@ +import { first } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { + isFunctionTypeNode, + isVariableDeclaration, +} from "../../compiler/factory/nodeTests"; import { - addToSeen, ArrowFunction, - Diagnostics, - factory, - findChildOfKind, - first, FunctionDeclaration, FunctionExpression, - getContainingFunction, - getEntityNameFromTypeNode, - getNodeId, - getTokenAtPosition, - isFunctionTypeNode, - isVariableDeclaration, MethodDeclaration, Node, SourceFile, SyntaxKind, - textChanges, TypeNode, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + addToSeen, + getContainingFunction, + getEntityNameFromTypeNode, + getNodeId, +} from "../../compiler/utilities"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + findChildOfKind, + getTokenAtPosition, +} from "../utilities"; const fixId = "fixAwaitInSyncFunction"; const errorCodes = [ @@ -38,7 +44,7 @@ registerCodeFix({ const { sourceFile, span } = context; const nodes = getNodes(sourceFile, span.start); if (!nodes) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, nodes)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, nodes)); return [createCodeFixAction(fixId, changes, Diagnostics.Add_async_modifier_to_containing_function, fixId, Diagnostics.Add_all_missing_async_modifiers)]; }, fixIds: [fixId], @@ -94,7 +100,7 @@ function getNodes(sourceFile: SourceFile, start: number): { insertBefore: Node, } function doChange( - changes: textChanges.ChangeTracker, + changes: ChangeTracker, sourceFile: SourceFile, { insertBefore, returnType }: { insertBefore: Node, returnType: TypeNode | undefined }): void { diff --git a/src/services/codefixes/fixCannotFindModule.ts b/src/services/codefixes/fixCannotFindModule.ts index d128045f7f075..27ae81fc07c40 100644 --- a/src/services/codefixes/fixCannotFindModule.ts +++ b/src/services/codefixes/fixCannotFindModule.ts @@ -1,22 +1,24 @@ +import { tryCast } from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { isStringLiteral } from "../../compiler/factory/nodeTests"; import { - Debug, - Diagnostics, - getTokenAtPosition, getTypesPackageName, - InstallPackageAction, - isExternalModuleNameRelative, - isStringLiteral, - JsTyping, - LanguageServiceHost, parsePackageName, - SourceFile, - tryCast, -} from "../_namespaces/ts"; +} from "../../compiler/moduleNameResolver"; +import { SourceFile } from "../../compiler/types"; +import { isExternalModuleNameRelative } from "../../compiler/utilitiesPublic"; +import { nodeCoreModules } from "../../jsTyping/jsTyping"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { + InstallPackageAction, + LanguageServiceHost, +} from "../types"; +import { getTokenAtPosition } from "../utilities"; const fixName = "fixCannotFindModule"; const fixIdInstallTypesPackage = "installTypesPackage"; @@ -71,6 +73,6 @@ function tryGetImportedPackageName(sourceFile: SourceFile, pos: number): string function getTypesPackageNameToInstall(packageName: string, host: LanguageServiceHost, diagCode: number): string | undefined { return diagCode === errorCodeCannotFindModule - ? (JsTyping.nodeCoreModules.has(packageName) ? "@types/node" : undefined) + ? (nodeCoreModules.has(packageName) ? "@types/node" : undefined) : (host.isKnownTypesPackageName?.(packageName) ? getTypesPackageName(packageName) : undefined); } diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index 80649e31bc5dd..9fa93841404d3 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -1,29 +1,35 @@ import { - addToSeen, cast, + first, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { ClassElement, ClassLikeDeclaration, - Diagnostics, - first, - getEffectiveBaseTypeNode, - getNodeId, - getSyntacticModifierFlags, - getTokenAtPosition, - isClassLike, ModifierFlags, SourceFile, Symbol, - textChanges, UserPreferences, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + addToSeen, + getEffectiveBaseTypeNode, + getNodeId, + getSyntacticModifierFlags, +} from "../../compiler/utilities"; +import { isClassLike } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, - createImportAdder, - createMissingMemberNodes, registerCodeFix, +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; +import { + createMissingMemberNodes, TypeConstructionContext, -} from "../_namespaces/ts.codefix"; +} from "./helpers"; +import { createImportAdder } from "./importAdder"; const errorCodes = [ Diagnostics.Non_abstract_class_0_does_not_implement_all_abstract_members_of_1.code, @@ -33,7 +39,7 @@ registerCodeFix({ errorCodes, getCodeActions: function getCodeActionsToFixClassNotImplementingInheritedMembers(context) { const { sourceFile, span } = context; - const changes = textChanges.ChangeTracker.with(context, t => + const changes = ChangeTracker.with(context, t => addMissingMembers(getClass(sourceFile, span.start), sourceFile, context, t, context.preferences)); return changes.length === 0 ? undefined : [createCodeFixAction(fixId, changes, Diagnostics.Implement_inherited_abstract_class, fixId, Diagnostics.Implement_all_inherited_abstract_classes)]; }, @@ -56,7 +62,7 @@ function getClass(sourceFile: SourceFile, pos: number): ClassLikeDeclaration { return cast(token.parent, isClassLike); } -function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, context: TypeConstructionContext, changeTracker: textChanges.ChangeTracker, preferences: UserPreferences): void { +function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, context: TypeConstructionContext, changeTracker: ChangeTracker, preferences: UserPreferences): void { const extendsNode = getEffectiveBaseTypeNode(classDeclaration)!; const checker = context.program.getTypeChecker(); const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode); @@ -65,7 +71,7 @@ function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: S // so duplicates cannot occur. const abstractAndNonPrivateExtendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType).filter(symbolPointsToNonPrivateAndAbstractMember); - const importAdder = createImportAdder(sourceFile, context.program, preferences, context.host); + const importAdder = createImportAdder(sourceFile, context.program, preferences, context.host, /*useAutoImportProvider*/ false); createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, sourceFile, context, preferences, importAdder, member => changeTracker.insertMemberAtStart(sourceFile, classDeclaration, member as ClassElement)); importAdder.writeFixes(changeTracker); } diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index f91c49c9bece9..81b1a9ed6ad8e 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -1,42 +1,48 @@ import { - addToSeen, and, + find, + mapDefined, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { isConstructorDeclaration } from "../../compiler/factory/nodeTests"; +import { ClassElement, ClassLikeDeclaration, - CodeFixAction, - createSymbolTable, - Debug, - Diagnostics, ExpressionWithTypeArguments, - find, - getContainingClass, - getEffectiveBaseTypeNode, - getEffectiveImplementsTypeNodes, - getEffectiveModifierFlags, - getNodeId, - getTokenAtPosition, IndexKind, InterfaceDeclaration, InterfaceType, - isConstructorDeclaration, - mapDefined, ModifierFlags, SourceFile, Symbol, SymbolTable, - textChanges, TypeChecker, UserPreferences, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + addToSeen, + createSymbolTable, + getContainingClass, + getEffectiveBaseTypeNode, + getEffectiveImplementsTypeNodes, + getEffectiveModifierFlags, + getNodeId, +} from "../../compiler/utilities"; import { codeFixAll, createCodeFixAction, - createImportAdder, + registerCodeFix, +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { CodeFixAction } from "../types"; +import { getTokenAtPosition } from "../utilities"; +import { createMissingMemberNodes, getNoopSymbolTrackerWithResolver, - registerCodeFix, TypeConstructionContext, -} from "../_namespaces/ts.codefix"; +} from "./helpers"; +import { createImportAdder } from "./importAdder"; const errorCodes = [ Diagnostics.Class_0_incorrectly_implements_interface_1.code, @@ -49,7 +55,7 @@ registerCodeFix({ const { sourceFile, span } = context; const classDeclaration = getClass(sourceFile, span.start); return mapDefined(getEffectiveImplementsTypeNodes(classDeclaration), implementedTypeNode => { - const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(context, implementedTypeNode, sourceFile, classDeclaration, t, context.preferences)); + const changes = ChangeTracker.with(context, t => addMissingDeclarations(context, implementedTypeNode, sourceFile, classDeclaration, t, context.preferences)); return changes.length === 0 ? undefined : createCodeFixAction(fixId, changes, [Diagnostics.Implement_interface_0, implementedTypeNode.getText(sourceFile)], fixId, Diagnostics.Implement_all_unimplemented_interfaces); }); }, @@ -80,7 +86,7 @@ function addMissingDeclarations( implementedTypeNode: ExpressionWithTypeArguments, sourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, - changeTracker: textChanges.ChangeTracker, + changeTracker: ChangeTracker, preferences: UserPreferences, ): void { const checker = context.program.getTypeChecker(); @@ -101,7 +107,7 @@ function addMissingDeclarations( createMissingIndexSignatureDeclaration(implementedType, IndexKind.String); } - const importAdder = createImportAdder(sourceFile, context.program, preferences, context.host); + const importAdder = createImportAdder(sourceFile, context.program, preferences, context.host, /*useAutoImportProvider*/ false); createMissingMemberNodes(classDeclaration, nonPrivateAndNotExistedInHeritageClauseMembers, sourceFile, context, preferences, importAdder, member => insertInterfaceMemberNode(sourceFile, classDeclaration, member as ClassElement)); importAdder.writeFixes(changeTracker); diff --git a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts index 02276f8d7a482..c17e1f3a7b9f2 100644 --- a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts +++ b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts @@ -1,27 +1,31 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { + isExpressionStatement, + isPropertyAccessExpression, +} from "../../compiler/factory/nodeTests"; +import { forEachChild } from "../../compiler/parser"; import { - addToSeen, CallExpression, ConstructorDeclaration, - Diagnostics, ExpressionStatement, - forEachChild, - getContainingFunction, - getNodeId, - getTokenAtPosition, - isExpressionStatement, - isFunctionLike, - isPropertyAccessExpression, - isSuperCall, Node, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + addToSeen, + getContainingFunction, + getNodeId, + isSuperCall, +} from "../../compiler/utilities"; +import { isFunctionLike } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "classSuperMustPrecedeThisAccess"; const errorCodes = [Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class.code]; @@ -32,7 +36,7 @@ registerCodeFix({ const nodes = getNodes(sourceFile, span.start); if (!nodes) return undefined; const { constructor, superCall } = nodes; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall)); return [createCodeFixAction(fixId, changes, Diagnostics.Make_super_call_the_first_statement_in_the_constructor, fixId, Diagnostics.Make_all_super_calls_the_first_statement_in_their_constructor)]; }, fixIds: [fixId], @@ -50,7 +54,7 @@ registerCodeFix({ }, }); -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, constructor: ConstructorDeclaration, superCall: ExpressionStatement): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, constructor: ConstructorDeclaration, superCall: ExpressionStatement): void { changes.insertNodeAtConstructorStart(sourceFile, constructor, superCall); changes.delete(sourceFile, superCall); } diff --git a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts index feee99da0d0cf..a3a58e76e9898 100644 --- a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts +++ b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts @@ -1,19 +1,19 @@ +import { emptyArray } from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isConstructorDeclaration } from "../../compiler/factory/nodeTests"; import { ConstructorDeclaration, - Debug, - Diagnostics, - emptyArray, - factory, - getTokenAtPosition, - isConstructorDeclaration, SourceFile, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "constructorForDerivedNeedSuperCall"; const errorCodes = [Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call.code]; @@ -22,7 +22,7 @@ registerCodeFix({ getCodeActions(context) { const { sourceFile, span } = context; const ctr = getNode(sourceFile, span.start); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, ctr)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, ctr)); return [createCodeFixAction(fixId, changes, Diagnostics.Add_missing_super_call, fixId, Diagnostics.Add_all_missing_super_calls)]; }, fixIds: [fixId], @@ -36,7 +36,7 @@ function getNode(sourceFile: SourceFile, pos: number): ConstructorDeclaration { return token.parent; } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, ctr: ConstructorDeclaration) { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, ctr: ConstructorDeclaration) { const superCall = factory.createExpressionStatement(factory.createCallExpression(factory.createSuper(), /*typeArguments*/ undefined, /*argumentsArray*/ emptyArray)); changes.insertNodeAtConstructorStart(sourceFile, ctr, superCall); } diff --git a/src/services/codefixes/fixEnableJsxFlag.ts b/src/services/codefixes/fixEnableJsxFlag.ts index 4870e021120f7..db6e60e48a460 100644 --- a/src/services/codefixes/fixEnableJsxFlag.ts +++ b/src/services/codefixes/fixEnableJsxFlag.ts @@ -1,15 +1,13 @@ -import { - Diagnostics, - factory, - textChanges, - TsConfigSourceFile, -} from "../_namespaces/ts"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { TsConfigSourceFile } from "../../compiler/types"; import { codeFixAll, createCodeFixActionWithoutFixAll, registerCodeFix, - setJsonCompilerOptionValue, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { setJsonCompilerOptionValue } from "./helpers"; const fixID = "fixEnableJsxFlag"; const errorCodes = [Diagnostics.Cannot_use_JSX_unless_the_jsx_flag_is_provided.code]; @@ -21,7 +19,7 @@ registerCodeFix({ return undefined; } - const changes = textChanges.ChangeTracker.with(context, changeTracker => + const changes = ChangeTracker.with(context, changeTracker => doChange(changeTracker, configFile) ); return [ @@ -40,6 +38,6 @@ registerCodeFix({ }) }); -function doChange(changeTracker: textChanges.ChangeTracker, configFile: TsConfigSourceFile) { +function doChange(changeTracker: ChangeTracker, configFile: TsConfigSourceFile) { setJsonCompilerOptionValue(changeTracker, configFile, "jsx", factory.createStringLiteral("react")); } diff --git a/src/services/codefixes/fixExpectedComma.ts b/src/services/codefixes/fixExpectedComma.ts index ed571ab443352..eed0d497e60da 100644 --- a/src/services/codefixes/fixExpectedComma.ts +++ b/src/services/codefixes/fixExpectedComma.ts @@ -1,19 +1,21 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - Diagnostics, - factory, - getTokenAtPosition, isArrayLiteralExpression, isObjectLiteralExpression, +} from "../../compiler/factory/nodeTests"; +import { Node, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "fixExpectedComma"; const expectedErrorCode = Diagnostics._0_expected.code; @@ -26,7 +28,7 @@ registerCodeFix({ const info = getInfo(sourceFile, context.span.start, context.errorCode); if (!info) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, info)); return [createCodeFixAction( fixId, @@ -54,7 +56,7 @@ function getInfo(sourceFile: SourceFile, pos: number, _: number): Info | undefin isArrayLiteralExpression(node.parent))) ? { node } : undefined; } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { node }: Info): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, { node }: Info): void { const newNode = factory.createToken(SyntaxKind.CommaToken); changes.replaceNode(sourceFile, node, newNode); } diff --git a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts index 7beffebd59986..b0b1b0f4b65ca 100644 --- a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts +++ b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts @@ -1,20 +1,20 @@ +import { isWhiteSpaceSingleLine } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - Diagnostics, - factory, - getContainingClass, - getTokenAtPosition, HeritageClause, - isWhiteSpaceSingleLine, Node, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { getContainingClass } from "../../compiler/utilities"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "extendsInterfaceBecomesImplements"; const errorCodes = [Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements.code]; @@ -25,7 +25,7 @@ registerCodeFix({ const nodes = getNodes(sourceFile, context.span.start); if (!nodes) return undefined; const { extendsToken, heritageClauses } = nodes; - const changes = textChanges.ChangeTracker.with(context, t => doChanges(t, sourceFile, extendsToken, heritageClauses)); + const changes = ChangeTracker.with(context, t => doChanges(t, sourceFile, extendsToken, heritageClauses)); return [createCodeFixAction(fixId, changes, Diagnostics.Change_extends_to_implements, fixId, Diagnostics.Change_all_extended_interfaces_to_implements)]; }, fixIds: [fixId], @@ -42,7 +42,7 @@ function getNodes(sourceFile: SourceFile, pos: number) { return extendsToken.kind === SyntaxKind.ExtendsKeyword ? { extendsToken, heritageClauses } : undefined; } -function doChanges(changes: textChanges.ChangeTracker, sourceFile: SourceFile, extendsToken: Node, heritageClauses: readonly HeritageClause[]): void { +function doChanges(changes: ChangeTracker, sourceFile: SourceFile, extendsToken: Node, heritageClauses: readonly HeritageClause[]): void { changes.replaceNode(sourceFile, extendsToken, factory.createToken(SyntaxKind.ImplementsKeyword)); // If there is already an implements clause, replace the implements keyword with a comma. diff --git a/src/services/codefixes/fixForgottenThisPropertyAccess.ts b/src/services/codefixes/fixForgottenThisPropertyAccess.ts index ae8d0cd294f0b..89201df7a94a1 100644 --- a/src/services/codefixes/fixForgottenThisPropertyAccess.ts +++ b/src/services/codefixes/fixForgottenThisPropertyAccess.ts @@ -1,21 +1,25 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - Diagnostics, - factory, - getContainingClass, - getTokenAtPosition, - Identifier, isIdentifier, isPrivateIdentifier, +} from "../../compiler/factory/nodeTests"; +import { + Identifier, PrivateIdentifier, SourceFile, - suppressLeadingAndTrailingTrivia, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { getContainingClass } from "../../compiler/utilities"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + getTokenAtPosition, + suppressLeadingAndTrailingTrivia, +} from "../utilities"; const fixId = "forgottenThisPropertyAccess"; const didYouMeanStaticMemberCode = Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0.code; @@ -32,7 +36,7 @@ registerCodeFix({ if (!info) { return undefined; } - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, info)); return [createCodeFixAction(fixId, changes, [Diagnostics.Add_0_to_unresolved_variable, info.className || "this"], fixId, Diagnostics.Add_qualifier_to_all_unresolved_variables_matching_a_member_name)]; }, fixIds: [fixId], @@ -54,7 +58,7 @@ function getInfo(sourceFile: SourceFile, pos: number, diagCode: number): Info | } } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { node, className }: Info): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, { node, className }: Info): void { // TODO (https://github.com/Microsoft/TypeScript/issues/21246): use shared helper suppressLeadingAndTrailingTrivia(node); changes.replaceNode(sourceFile, node, factory.createPropertyAccessExpression(className ? factory.createIdentifier(className) : factory.createThis(), node)); diff --git a/src/services/codefixes/fixImplicitThis.ts b/src/services/codefixes/fixImplicitThis.ts index 86c8adad40bd3..a9897dfec289d 100644 --- a/src/services/codefixes/fixImplicitThis.ts +++ b/src/services/codefixes/fixImplicitThis.ts @@ -1,28 +1,32 @@ +import { emptyArray } from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - ANONYMOUS, - Debug, - DiagnosticOrDiagnosticAndArguments, - Diagnostics, - emptyArray, - factory, - FindAllReferences, - findChildOfKind, - getThisContainer, - getTokenAtPosition, isFunctionDeclaration, isFunctionExpression, isSourceFile, - isThis, +} from "../../compiler/factory/nodeTests"; +import { SourceFile, SyntaxKind, - textChanges, TypeChecker, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { getThisContainer } from "../../compiler/utilities"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { Core as FindAllReferences } from "../findAllReferences"; +import { ChangeTracker } from "../textChanges"; +import { + ANONYMOUS, + DiagnosticOrDiagnosticAndArguments, + findChildOfKind, + getTokenAtPosition, + isThis, +} from "../utilities"; const fixId = "fixImplicitThis"; const errorCodes = [Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation.code]; @@ -31,7 +35,7 @@ registerCodeFix({ getCodeActions: function getCodeActionsToFixImplicitThis(context) { const { sourceFile, program, span } = context; let diagnostic: DiagnosticOrDiagnosticAndArguments | undefined; - const changes = textChanges.ChangeTracker.with(context, t => { + const changes = ChangeTracker.with(context, t => { diagnostic = doChange(t, sourceFile, span.start, program.getTypeChecker()); }); return diagnostic ? [createCodeFixAction(fixId, changes, diagnostic, fixId, Diagnostics.Fix_all_implicit_this_errors)] : emptyArray; @@ -42,7 +46,7 @@ registerCodeFix({ }), }); -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number, checker: TypeChecker): DiagnosticOrDiagnosticAndArguments | undefined { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, pos: number, checker: TypeChecker): DiagnosticOrDiagnosticAndArguments | undefined { const token = getTokenAtPosition(sourceFile, pos); if (!isThis(token)) return undefined; @@ -54,7 +58,7 @@ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, po const { name } = fn; const body = Debug.checkDefined(fn.body); // Should be defined because the function contained a 'this' expression if (isFunctionExpression(fn)) { - if (name && FindAllReferences.Core.isSymbolReferencedInFile(name, checker, sourceFile, body)) { + if (name && FindAllReferences.isSymbolReferencedInFile(name, checker, sourceFile, body)) { // Function expression references itself. To fix we would have to extract it to a const. return undefined; } diff --git a/src/services/codefixes/fixImportNonExportedMember.ts b/src/services/codefixes/fixImportNonExportedMember.ts index ba25afcbf5505..9845e26615237 100644 --- a/src/services/codefixes/fixImportNonExportedMember.ts +++ b/src/services/codefixes/fixImportNonExportedMember.ts @@ -1,43 +1,50 @@ import { - canHaveExportModifier, - canHaveLocals, - Declaration, - Diagnostics, - ExportDeclaration, - factory, find, - findAncestor, findLast, firstOrUndefined, - getIsolatedModules, - getResolvedModule, - getTokenAtPosition, - Identifier, + length, + map, + tryCast, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isExportDeclaration, isIdentifier, isImportDeclaration, isNamedExports, - isSourceFileFromLibrary, isStringLiteral, - isTypeDeclaration, isVariableDeclaration, isVariableStatement, - length, - map, +} from "../../compiler/factory/nodeTests"; +import { + Declaration, + ExportDeclaration, + Identifier, Node, Program, SourceFile, Symbol, - textChanges, - tryCast, VariableStatement, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + canHaveExportModifier, + getIsolatedModules, + getResolvedModule, + isTypeDeclaration, +} from "../../compiler/utilities"; +import { canHaveLocals, findAncestor } from "../../compiler/utilitiesPublic"; import { createCodeFixAction, createCombinedCodeActions, eachDiagnostic, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + getTokenAtPosition, + isSourceFileFromLibrary, +} from "../utilities"; const fixId = "fixImportNonExportedMember"; const errorCodes = [ @@ -52,12 +59,12 @@ registerCodeFix({ const info = getInfo(sourceFile, span.start, program); if (info === undefined) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, program, info)); + const changes = ChangeTracker.with(context, t => doChange(t, program, info)); return [createCodeFixAction(fixId, changes, [Diagnostics.Export_0_from_module_1, info.exportName.node.text, info.moduleSpecifier], fixId, Diagnostics.Export_all_referenced_locals)]; }, getAllCodeActions(context) { const { program } = context; - return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => { + return createCombinedCodeActions(ChangeTracker.with(context, changes => { const exports = new Map(); eachDiagnostic(context, errorCodes, diag => { @@ -142,7 +149,7 @@ function getInfo(sourceFile: SourceFile, pos: number, program: Program): Info | return undefined; } -function doChange(changes: textChanges.ChangeTracker, program: Program, { exportName, node, moduleSourceFile }: Info) { +function doChange(changes: ChangeTracker, program: Program, { exportName, node, moduleSourceFile }: Info) { const exportDeclaration = tryGetExportDeclaration(moduleSourceFile, exportName.isTypeOnly); if (exportDeclaration) { updateExport(changes, program, moduleSourceFile, exportDeclaration, [exportName]); @@ -155,7 +162,7 @@ function doChange(changes: textChanges.ChangeTracker, program: Program, { export } } -function doChanges(changes: textChanges.ChangeTracker, program: Program, sourceFile: SourceFile, moduleExports: ExportName[], node: ExportDeclaration | undefined) { +function doChanges(changes: ChangeTracker, program: Program, sourceFile: SourceFile, moduleExports: ExportName[], node: ExportDeclaration | undefined) { if (length(moduleExports)) { if (node) { updateExport(changes, program, sourceFile, node, moduleExports); @@ -172,7 +179,7 @@ function tryGetExportDeclaration(sourceFile: SourceFile, isTypeOnly: boolean) { return findLast(sourceFile.statements, predicate); } -function updateExport(changes: textChanges.ChangeTracker, program: Program, sourceFile: SourceFile, node: ExportDeclaration, names: ExportName[]) { +function updateExport(changes: ChangeTracker, program: Program, sourceFile: SourceFile, node: ExportDeclaration, names: ExportName[]) { const namedExports = node.exportClause && isNamedExports(node.exportClause) ? node.exportClause.elements : factory.createNodeArray([]); const allowTypeModifier = !node.isTypeOnly && !!(getIsolatedModules(program.getCompilerOptions()) || find(namedExports, e => e.isTypeOnly)); changes.replaceNode(sourceFile, node, @@ -181,7 +188,7 @@ function updateExport(changes: textChanges.ChangeTracker, program: Program, sour factory.createNodeArray([...namedExports, ...createExportSpecifiers(names, allowTypeModifier)], /*hasTrailingComma*/ namedExports.hasTrailingComma)), node.moduleSpecifier, node.assertClause)); } -function createExport(changes: textChanges.ChangeTracker, program: Program, sourceFile: SourceFile, names: ExportName[]) { +function createExport(changes: ChangeTracker, program: Program, sourceFile: SourceFile, names: ExportName[]) { changes.insertNodeAtEndOfScope(sourceFile, sourceFile, factory.createExportDeclaration(/*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports(createExportSpecifiers(names, /*allowTypeModifier*/ getIsolatedModules(program.getCompilerOptions()))), /*moduleSpecifier*/ undefined, /*assertClause*/ undefined)); diff --git a/src/services/codefixes/fixIncorrectNamedTupleSyntax.ts b/src/services/codefixes/fixIncorrectNamedTupleSyntax.ts index f7e8345b217f0..33b6eb78adee6 100644 --- a/src/services/codefixes/fixIncorrectNamedTupleSyntax.ts +++ b/src/services/codefixes/fixIncorrectNamedTupleSyntax.ts @@ -1,20 +1,20 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - Diagnostics, - factory, - findAncestor, - getTokenAtPosition, NamedTupleMember, OptionalTypeNode, ParenthesizedTypeNode, RestTypeNode, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { findAncestor } from "../../compiler/utilitiesPublic"; import { createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "fixIncorrectNamedTupleSyntax"; const errorCodes = [ @@ -27,7 +27,7 @@ registerCodeFix({ getCodeActions: function getCodeActionsToFixIncorrectNamedTupleSyntax(context) { const { sourceFile, span } = context; const namedTupleMember = getNamedTupleMember(sourceFile, span.start); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, namedTupleMember)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, namedTupleMember)); return [createCodeFixAction(fixId, changes, Diagnostics.Move_labeled_tuple_element_modifiers_to_labels, fixId, Diagnostics.Move_labeled_tuple_element_modifiers_to_labels)]; }, fixIds: [fixId] @@ -37,7 +37,7 @@ function getNamedTupleMember(sourceFile: SourceFile, pos: number) { const token = getTokenAtPosition(sourceFile, pos); return findAncestor(token, t => t.kind === SyntaxKind.NamedTupleMember) as NamedTupleMember | undefined; } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, namedTupleMember?: NamedTupleMember) { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, namedTupleMember?: NamedTupleMember) { if (!namedTupleMember) { return; } diff --git a/src/services/codefixes/fixInvalidImportSyntax.ts b/src/services/codefixes/fixInvalidImportSyntax.ts index bae3f2d2c0f16..ce3b23718732d 100644 --- a/src/services/codefixes/fixInvalidImportSyntax.ts +++ b/src/services/codefixes/fixInvalidImportSyntax.ts @@ -1,34 +1,42 @@ +import { addRange } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - addRange, CallExpression, - CodeFixAction, - CodeFixContext, - Diagnostics, - factory, - findAncestor, - getEmitModuleKind, - getNamespaceDeclarationNode, - getQuotePreference, - getSourceFileOfNode, - getTokenAtPosition, ImportDeclaration, - isExpression, - isImportCall, - isNamedDeclaration, - isTransientSymbol, - makeImport, ModuleKind, NamespaceImport, NewExpression, Node, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getEmitModuleKind, + getNamespaceDeclarationNode, + getSourceFileOfNode, + isImportCall, + isTransientSymbol, +} from "../../compiler/utilities"; +import { + findAncestor, + isExpression, + isNamedDeclaration, +} from "../../compiler/utilitiesPublic"; import { createCodeFixActionWithoutFixAll, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + CodeFixAction, + CodeFixContext, +} from "../types"; +import { + getQuotePreference, + getTokenAtPosition, + makeImport, +} from "../utilities"; const fixName = "invalidImportSyntax"; @@ -55,7 +63,7 @@ function getCodeFixesForImportDeclaration(context: CodeFixContext, node: ImportD } function createAction(context: CodeFixContext, sourceFile: SourceFile, node: Node, replacement: Node): CodeFixAction { - const changes = textChanges.ChangeTracker.with(context, t => t.replaceNode(sourceFile, node, replacement)); + const changes = ChangeTracker.with(context, t => t.replaceNode(sourceFile, node, replacement)); return createCodeFixActionWithoutFixAll(fixName, changes, [Diagnostics.Replace_import_with_0, changes[0].textChanges[0].newText]); } @@ -117,7 +125,7 @@ function getImportCodeFixesForExpression(context: CodeFixContext, expr: Node): C } if (isExpression(expr) && !(isNamedDeclaration(expr.parent) && expr.parent.name === expr)) { const sourceFile = context.sourceFile; - const changes = textChanges.ChangeTracker.with(context, t => t.replaceNode(sourceFile, expr, factory.createPropertyAccessExpression(expr, "default"), {})); + const changes = ChangeTracker.with(context, t => t.replaceNode(sourceFile, expr, factory.createPropertyAccessExpression(expr, "default"), {})); fixes.push(createCodeFixActionWithoutFixAll(fixName, changes, Diagnostics.Use_synthetic_default_member)); } return fixes; diff --git a/src/services/codefixes/fixInvalidJsxCharacters.ts b/src/services/codefixes/fixInvalidJsxCharacters.ts index aea34c0c42cef..e9e91e55a3eb2 100644 --- a/src/services/codefixes/fixInvalidJsxCharacters.ts +++ b/src/services/codefixes/fixInvalidJsxCharacters.ts @@ -1,16 +1,16 @@ +import { hasProperty } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; import { - Diagnostics, - hasProperty, - quote, SourceFile, - textChanges, UserPreferences, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { quote } from "../utilities"; const fixIdExpression = "fixInvalidJsxCharacters_expression"; const fixIdHtmlEntity = "fixInvalidJsxCharacters_htmlEntity"; @@ -25,8 +25,8 @@ registerCodeFix({ fixIds: [fixIdExpression, fixIdHtmlEntity], getCodeActions(context) { const { sourceFile, preferences, span } = context; - const changeToExpression = textChanges.ChangeTracker.with(context, t => doChange(t, preferences, sourceFile, span.start, /*useHtmlEntity*/ false)); - const changeToHtmlEntity = textChanges.ChangeTracker.with(context, t => doChange(t, preferences, sourceFile, span.start, /*useHtmlEntity*/ true)); + const changeToExpression = ChangeTracker.with(context, t => doChange(t, preferences, sourceFile, span.start, /*useHtmlEntity*/ false)); + const changeToHtmlEntity = ChangeTracker.with(context, t => doChange(t, preferences, sourceFile, span.start, /*useHtmlEntity*/ true)); return [ createCodeFixAction(fixIdExpression, changeToExpression, Diagnostics.Wrap_invalid_character_in_an_expression_container, fixIdExpression, Diagnostics.Wrap_all_invalid_characters_in_an_expression_container), @@ -47,7 +47,7 @@ function isValidCharacter(character: string): character is keyof typeof htmlEnti return hasProperty(htmlEntity, character); } -function doChange(changes: textChanges.ChangeTracker, preferences: UserPreferences, sourceFile: SourceFile, start: number, useHtmlEntity: boolean) { +function doChange(changes: ChangeTracker, preferences: UserPreferences, sourceFile: SourceFile, start: number, useHtmlEntity: boolean) { const character = sourceFile.getText()[start]; // sanity check if (!isValidCharacter(character)) { diff --git a/src/services/codefixes/fixJSDocTypes.ts b/src/services/codefixes/fixJSDocTypes.ts index 3a50e7579675c..1615311c5feb9 100644 --- a/src/services/codefixes/fixJSDocTypes.ts +++ b/src/services/codefixes/fixJSDocTypes.ts @@ -1,17 +1,14 @@ +import { append } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { isJSDocNullableType } from "../../compiler/factory/nodeTests"; import { - append, AsExpression, CallSignatureDeclaration, - CodeFixAction, ConstructSignatureDeclaration, DiagnosticMessage, - Diagnostics, - findAncestor, FunctionDeclaration, GetAccessorDeclaration, - getTokenAtPosition, IndexSignatureDeclaration, - isJSDocNullableType, MappedTypeNode, MethodDeclaration, MethodSignature, @@ -22,7 +19,6 @@ import { SetAccessorDeclaration, SourceFile, SyntaxKind, - textChanges, Type, TypeAliasDeclaration, TypeAssertion, @@ -30,12 +26,16 @@ import { TypeFlags, TypeNode, VariableDeclaration, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { findAncestor } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { CodeFixAction } from "../types"; +import { getTokenAtPosition } from "../utilities"; const fixIdPlain = "fixJSDocTypes_plain"; const fixIdNullable = "fixJSDocTypes_nullable"; @@ -63,7 +63,7 @@ registerCodeFix({ return actions; function fix(type: Type, fixId: string, fixAllDescription: DiagnosticMessage): CodeFixAction { - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, typeNode, type, checker)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, typeNode, type, checker)); return createCodeFixAction("jdocTypes", changes, [Diagnostics.Change_0_to_1, original, checker.typeToString(type)], fixId, fixAllDescription); } }, @@ -81,7 +81,7 @@ registerCodeFix({ } }); -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, oldTypeNode: TypeNode, newType: Type, checker: TypeChecker): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, oldTypeNode: TypeNode, newType: Type, checker: TypeChecker): void { changes.replaceNode(sourceFile, oldTypeNode, checker.typeToTypeNode(newType, /*enclosingDeclaration*/ oldTypeNode, /*flags*/ undefined)!); // TODO: GH#18217 } diff --git a/src/services/codefixes/fixMissingCallParentheses.ts b/src/services/codefixes/fixMissingCallParentheses.ts index 4fcc89c87fcc0..61d3bc4865204 100644 --- a/src/services/codefixes/fixMissingCallParentheses.ts +++ b/src/services/codefixes/fixMissingCallParentheses.ts @@ -1,19 +1,21 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; import { - Diagnostics, - getTokenAtPosition, - Identifier, isIdentifier, isPropertyAccessExpression, +} from "../../compiler/factory/nodeTests"; +import { + Identifier, PrivateIdentifier, PropertyAccessExpression, SourceFile, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "fixMissingCallParentheses"; const errorCodes = [ @@ -28,7 +30,7 @@ registerCodeFix({ const callName = getCallName(sourceFile, span.start); if (!callName) return; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, callName)); + const changes = ChangeTracker.with(context, t => doChange(t, context.sourceFile, callName)); return [createCodeFixAction(fixId, changes, Diagnostics.Add_missing_call_parentheses, fixId, Diagnostics.Add_all_missing_call_parentheses)]; }, getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { @@ -37,7 +39,7 @@ registerCodeFix({ }) }); -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, name: Identifier | PrivateIdentifier): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, name: Identifier | PrivateIdentifier): void { changes.replaceNodeWithText(sourceFile, name, `${ name.text }()`); } diff --git a/src/services/codefixes/fixModuleAndTargetOptions.ts b/src/services/codefixes/fixModuleAndTargetOptions.ts index 9a223fa10d829..1d0e6b7d78bc2 100644 --- a/src/services/codefixes/fixModuleAndTargetOptions.ts +++ b/src/services/codefixes/fixModuleAndTargetOptions.ts @@ -1,21 +1,25 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - CodeFixAction, - Diagnostics, Expression, - factory, + ModuleKind, + ScriptTarget, +} from "../../compiler/types"; +import { getEmitModuleKind, getEmitScriptTarget, getTsConfigObjectLiteralExpression, - ModuleKind, - ScriptTarget, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/utilities"; import { createCodeFixActionWithoutFixAll, registerCodeFix, +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { CodeFixAction } from "../types"; +import { setJsonCompilerOptionValue, setJsonCompilerOptionValues, -} from "../_namespaces/ts.codefix"; +} from "./helpers"; registerCodeFix({ errorCodes: [ @@ -33,7 +37,7 @@ registerCodeFix({ const moduleKind = getEmitModuleKind(compilerOptions); const moduleOutOfRange = moduleKind >= ModuleKind.ES2015 && moduleKind < ModuleKind.ESNext; if (moduleOutOfRange) { - const changes = textChanges.ChangeTracker.with(context, changes => { + const changes = ChangeTracker.with(context, changes => { setJsonCompilerOptionValue(changes, configFile, "module", factory.createStringLiteral("esnext")); }); codeFixes.push(createCodeFixActionWithoutFixAll("fixModuleOption", changes, [Diagnostics.Set_the_module_option_in_your_configuration_file_to_0, "esnext"])); @@ -42,7 +46,7 @@ registerCodeFix({ const target = getEmitScriptTarget(compilerOptions); const targetOutOfRange = target < ScriptTarget.ES2017 || target > ScriptTarget.ESNext; if (targetOutOfRange) { - const changes = textChanges.ChangeTracker.with(context, tracker => { + const changes = ChangeTracker.with(context, tracker => { const configObject = getTsConfigObjectLiteralExpression(configFile); if (!configObject) return; diff --git a/src/services/codefixes/fixNaNEquality.ts b/src/services/codefixes/fixNaNEquality.ts index cf86a7ef2d53d..e444e47a32752 100644 --- a/src/services/codefixes/fixNaNEquality.ts +++ b/src/services/codefixes/fixNaNEquality.ts @@ -1,26 +1,28 @@ +import { find } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isBinaryExpression } from "../../compiler/factory/nodeTests"; +import { flattenDiagnosticMessageText } from "../../compiler/program"; import { BinaryExpression, - createTextSpan, DiagnosticMessageChain, - Diagnostics, Expression, - factory, - find, - flattenDiagnosticMessageText, - isBinaryExpression, - isExpression, Program, SourceFile, SyntaxKind, - textChanges, TextSpan, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + createTextSpan, + isExpression, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, - findAncestorMatchingSpan, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { findAncestorMatchingSpan } from "./helpers"; const fixId = "fixNaNEquality"; const errorCodes = [ @@ -35,7 +37,7 @@ registerCodeFix({ if (info === undefined) return; const { suggestion, expression, arg } = info; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, arg, expression)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, arg, expression)); return [createCodeFixAction(fixId, changes, [Diagnostics.Use_0, suggestion], fixId, Diagnostics.Use_Number_isNaN_in_all_conditions)]; }, fixIds: [fixId], @@ -71,7 +73,7 @@ function getInfo(program: Program, sourceFile: SourceFile, span: TextSpan): Info return undefined; } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, arg: Expression, expression: BinaryExpression) { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, arg: Expression, expression: BinaryExpression) { const callExpression = factory.createCallExpression( factory.createPropertyAccessExpression(factory.createIdentifier("Number"), factory.createIdentifier("isNaN")), /*typeArguments*/ undefined, [arg]); const operator = expression.operatorToken.kind ; diff --git a/src/services/codefixes/fixNoPropertyAccessFromIndexSignature.ts b/src/services/codefixes/fixNoPropertyAccessFromIndexSignature.ts index 7a35126032924..a65ca2632f756 100644 --- a/src/services/codefixes/fixNoPropertyAccessFromIndexSignature.ts +++ b/src/services/codefixes/fixNoPropertyAccessFromIndexSignature.ts @@ -1,22 +1,24 @@ +import { cast } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isPropertyAccessExpression } from "../../compiler/factory/nodeTests"; import { - cast, - Diagnostics, - factory, - getQuotePreference, - getTokenAtPosition, - isPropertyAccessChain, - isPropertyAccessExpression, PropertyAccessExpression, - QuotePreference, SourceFile, - textChanges, UserPreferences, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { isPropertyAccessChain } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + getQuotePreference, + getTokenAtPosition, + QuotePreference, +} from "../utilities"; const fixId = "fixNoPropertyAccessFromIndexSignature"; const errorCodes = [ @@ -29,14 +31,14 @@ registerCodeFix({ getCodeActions(context) { const { sourceFile, span, preferences } = context; const property = getPropertyAccessExpression(sourceFile, span.start); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, property, preferences)); + const changes = ChangeTracker.with(context, t => doChange(t, context.sourceFile, property, preferences)); return [createCodeFixAction(fixId, changes, [Diagnostics.Use_element_access_for_0, property.name.text], fixId, Diagnostics.Use_element_access_for_all_undeclared_properties)]; }, getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, getPropertyAccessExpression(diag.file, diag.start), context.preferences)) }); -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: PropertyAccessExpression, preferences: UserPreferences): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, node: PropertyAccessExpression, preferences: UserPreferences): void { const quotePreference = getQuotePreference(sourceFile, preferences); const argumentsExpression = factory.createStringLiteral(node.name.text, quotePreference === QuotePreference.Single); changes.replaceNode( diff --git a/src/services/codefixes/fixOverrideModifier.ts b/src/services/codefixes/fixOverrideModifier.ts index f76a8420e0699..5242e5cf363e1 100644 --- a/src/services/codefixes/fixOverrideModifier.ts +++ b/src/services/codefixes/fixOverrideModifier.ts @@ -1,42 +1,52 @@ import { - CodeFixAllContext, - CodeFixContext, - ConstructorDeclaration, - Debug, - DiagnosticMessage, - Diagnostics, emptyArray, - factory, find, - findAncestor, findLast, - GetAccessorDeclaration, - getTokenAtPosition, + not, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isAbstractModifier, - isAccessibilityModifier, - isClassLike, isDecorator, isJSDocOverrideTag, isOverrideModifier, - isParameterPropertyDeclaration, - isSourceFileJS, isStaticModifier, +} from "../../compiler/factory/nodeTests"; +import { skipTrivia } from "../../compiler/scanner"; +import { + ConstructorDeclaration, + DiagnosticMessage, + GetAccessorDeclaration, MethodDeclaration, Node, - not, - ParameterPropertyDeclaration, PropertyDeclaration, SetAccessorDeclaration, - skipTrivia, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { isSourceFileJS } from "../../compiler/utilities"; +import { + findAncestor, + isClassLike, + isParameterPropertyDeclaration, + ParameterPropertyDeclaration, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixActionMaybeFixAll, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + CodeFixAllContext, + CodeFixContext, +} from "../types"; +import { + getTokenAtPosition, + isAccessibilityModifier, +} from "../utilities"; const fixName = "fixOverrideModifier"; const fixAddOverrideId = "fixAddOverrideModifier"; @@ -130,7 +140,7 @@ registerCodeFix({ if (!info) return emptyArray; const { descriptions, fixId, fixAllDescriptions } = info; - const changes = textChanges.ChangeTracker.with(context, changes => dispatchChanges(changes, context, errorCode, span.start)); + const changes = ChangeTracker.with(context, changes => dispatchChanges(changes, context, errorCode, span.start)); return [ createCodeFixActionMaybeFixAll(fixName, changes, descriptions, fixId, fixAllDescriptions) @@ -150,7 +160,7 @@ registerCodeFix({ }); function dispatchChanges( - changeTracker: textChanges.ChangeTracker, + changeTracker: ChangeTracker, context: CodeFixContext | CodeFixAllContext, errorCode: number, pos: number) { @@ -171,7 +181,7 @@ function dispatchChanges( } } -function doAddOverrideModifierChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number) { +function doAddOverrideModifierChange(changeTracker: ChangeTracker, sourceFile: SourceFile, pos: number) { const classElement = findContainerClassElementLike(sourceFile, pos); if (isSourceFileJS(sourceFile)) { changeTracker.addJSDocTags(sourceFile, classElement, [factory.createJSDocOverrideTag(factory.createIdentifier("override"))]); @@ -190,7 +200,7 @@ function doAddOverrideModifierChange(changeTracker: textChanges.ChangeTracker, s changeTracker.insertModifierAt(sourceFile, modifierPos, SyntaxKind.OverrideKeyword, options); } -function doRemoveOverrideModifierChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number) { +function doRemoveOverrideModifierChange(changeTracker: ChangeTracker, sourceFile: SourceFile, pos: number) { const classElement = findContainerClassElementLike(sourceFile, pos); if (isSourceFileJS(sourceFile)) { changeTracker.filterJSDocTags(sourceFile, classElement, not(isJSDocOverrideTag)); diff --git a/src/services/codefixes/fixPropertyAssignment.ts b/src/services/codefixes/fixPropertyAssignment.ts index 2f1ef5a198044..f2e6721e47962 100644 --- a/src/services/codefixes/fixPropertyAssignment.ts +++ b/src/services/codefixes/fixPropertyAssignment.ts @@ -1,19 +1,21 @@ +import { cast } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - cast, - Diagnostics, - Expression, - factory, - getTokenAtPosition, isShorthandPropertyAssignment, +} from "../../compiler/factory/nodeTests"; +import { + Expression, ShorthandPropertyAssignment, SourceFile, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "fixPropertyAssignment"; const errorCodes = [ @@ -26,14 +28,14 @@ registerCodeFix({ getCodeActions(context) { const { sourceFile, span } = context; const property = getProperty(sourceFile, span.start); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, property)); + const changes = ChangeTracker.with(context, t => doChange(t, context.sourceFile, property)); return [createCodeFixAction(fixId, changes, [Diagnostics.Change_0_to_1, "=", ":"], fixId, [Diagnostics.Switch_each_misused_0_to_1, "=", ":"])]; }, getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, getProperty(diag.file, diag.start))) }); -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: ShorthandPropertyAssignment): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, node: ShorthandPropertyAssignment): void { changes.replaceNode(sourceFile, node, factory.createPropertyAssignment(node.name, node.objectAssignmentInitializer as Expression)); } diff --git a/src/services/codefixes/fixPropertyOverrideAccessor.ts b/src/services/codefixes/fixPropertyOverrideAccessor.ts index 954a90ba6f34a..bc6756ca8e1d0 100644 --- a/src/services/codefixes/fixPropertyOverrideAccessor.ts +++ b/src/services/codefixes/fixPropertyOverrideAccessor.ts @@ -1,24 +1,30 @@ +import { singleOrUndefined } from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { SourceFile } from "../../compiler/types"; import { - CodeFixAllContext, - CodeFixContext, - Debug, - Diagnostics, getSourceFileOfNode, getTextOfPropertyName, - getTokenAtPosition, +} from "../../compiler/utilities"; +import { isAccessor, isClassLike, - singleOrUndefined, - SourceFile, unescapeLeadingUnderscores, -} from "../_namespaces/ts"; +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, + registerCodeFix, +} from "../codeFixProvider"; +import { + CodeFixAllContext, + CodeFixContext, +} from "../types"; +import { getTokenAtPosition } from "../utilities"; +import { generateAccessorFromProperty, getAllSupers, - registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "./generateAccessors"; const errorCodes = [ Diagnostics._0_is_defined_as_an_accessor_in_class_1_but_is_overridden_here_in_2_as_an_instance_property.code, diff --git a/src/services/codefixes/fixReturnTypeInAsyncFunction.ts b/src/services/codefixes/fixReturnTypeInAsyncFunction.ts index a7071d6e143f4..87848c52faf19 100644 --- a/src/services/codefixes/fixReturnTypeInAsyncFunction.ts +++ b/src/services/codefixes/fixReturnTypeInAsyncFunction.ts @@ -1,21 +1,23 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - Diagnostics, - factory, - findAncestor, - getTokenAtPosition, - isFunctionLikeDeclaration, - isInJSFile, SourceFile, - textChanges, Type, TypeChecker, TypeNode, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { isInJSFile } from "../../compiler/utilities"; +import { + findAncestor, + isFunctionLikeDeclaration, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "fixReturnTypeInAsyncFunction"; const errorCodes = [ @@ -40,7 +42,7 @@ registerCodeFix({ return undefined; } const { returnTypeNode, returnType, promisedTypeNode, promisedType } = info; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, returnTypeNode, promisedTypeNode)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, returnTypeNode, promisedTypeNode)); return [createCodeFixAction( fixId, changes, [Diagnostics.Replace_0_with_Promise_1, @@ -75,6 +77,6 @@ function getInfo(sourceFile: SourceFile, checker: TypeChecker, pos: number): Inf } } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, returnTypeNode: TypeNode, promisedTypeNode: TypeNode): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, returnTypeNode: TypeNode, promisedTypeNode: TypeNode): void { changes.replaceNode(sourceFile, returnTypeNode, factory.createTypeReferenceNode("Promise", [promisedTypeNode])); } diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 8e3574aa60cde..23f5c66727f50 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -1,50 +1,58 @@ +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - CodeFixContextBase, - Debug, - Diagnostics, - factory, - findAncestor, - getEffectiveBaseTypeNode, - getEmitScriptTarget, - getMeaningFromLocation, - getModeForUsageLocation, - getResolvedModule, - getTextOfNode, - getTokenAtPosition, - hasSyntacticModifier, - ImportDeclaration, isBinaryExpression, - isClassElement, - isClassLike, isIdentifier, - isIdentifierText, isImportDeclaration, isImportSpecifier, isJsxAttribute, - isJsxOpeningLikeElement, - isMemberName, - isNamedDeclaration, isPrivateIdentifier, isPropertyAccessExpression, isQualifiedName, - isStringLiteralLike, +} from "../../compiler/factory/nodeTests"; +import { getModeForUsageLocation } from "../../compiler/program"; +import { isIdentifierText } from "../../compiler/scanner"; +import { + ImportDeclaration, ModifierFlags, Node, NodeFlags, ScriptTarget, - SemanticMeaning, SourceFile, Symbol, SymbolFlags, - symbolName, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getEffectiveBaseTypeNode, + getEmitScriptTarget, + getResolvedModule, + getTextOfNode, + hasSyntacticModifier, +} from "../../compiler/utilities"; +import { + findAncestor, + isClassElement, + isClassLike, + isJsxOpeningLikeElement, + isMemberName, + isNamedDeclaration, + isStringLiteralLike, + symbolName, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { CodeFixContextBase } from "../types"; +import { + getMeaningFromLocation, + getTokenAtPosition, + SemanticMeaning, +} from "../utilities"; const fixId = "fixSpelling"; const errorCodes = [ @@ -71,7 +79,7 @@ registerCodeFix({ if (!info) return undefined; const { node, suggestedSymbol } = info; const target = getEmitScriptTarget(context.host.getCompilationSettings()); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node, suggestedSymbol, target)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, node, suggestedSymbol, target)); return [createCodeFixAction("spelling", changes, [Diagnostics.Change_spelling_to_0, symbolName(suggestedSymbol)], fixId, Diagnostics.Fix_all_detected_spelling_errors)]; }, fixIds: [fixId], @@ -146,7 +154,7 @@ function getInfo(sourceFile: SourceFile, pos: number, context: CodeFixContextBas return suggestedSymbol === undefined ? undefined : { node, suggestedSymbol }; } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: Node, suggestedSymbol: Symbol, target: ScriptTarget) { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, node: Node, suggestedSymbol: Symbol, target: ScriptTarget) { const suggestion = symbolName(suggestedSymbol); if (!isIdentifierText(suggestion, target) && isPropertyAccessExpression(node.parent)) { const valDecl = suggestedSymbol.valueDeclaration; diff --git a/src/services/codefixes/fixStrictClassInitialization.ts b/src/services/codefixes/fixStrictClassInitialization.ts index fa78042e08217..59405988f602e 100644 --- a/src/services/codefixes/fixStrictClassInitialization.ts +++ b/src/services/codefixes/fixStrictClassInitialization.ts @@ -1,38 +1,48 @@ import { append, - BigIntLiteralType, - CodeFixAction, - CodeFixContext, - Debug, - Diagnostics, - Expression, - factory, firstDefined, - getClassLikeDeclarationOfSymbol, - getEffectiveTypeAnnotationNode, - getFirstConstructorWithBody, - getTokenAtPosition, - hasSyntacticModifier, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isIdentifier, - isInJSFile, isPropertyDeclaration, isUnionTypeNode, +} from "../../compiler/factory/nodeTests"; +import { + BigIntLiteralType, + Expression, ModifierFlags, PropertyDeclaration, SourceFile, - suppressLeadingAndTrailingTrivia, SyntaxKind, - textChanges, Type, TypeChecker, TypeFlags, TypeNode, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getClassLikeDeclarationOfSymbol, + getEffectiveTypeAnnotationNode, + getFirstConstructorWithBody, + hasSyntacticModifier, + isInJSFile, +} from "../../compiler/utilities"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + CodeFixAction, + CodeFixContext, +} from "../types"; +import { + getTokenAtPosition, + suppressLeadingAndTrailingTrivia, +} from "../utilities"; const fixName = "strictClassInitialization"; const fixIdAddDefiniteAssignmentAssertions = "addMissingPropertyDefiniteAssignmentAssertions"; @@ -96,11 +106,11 @@ function getInfo(sourceFile: SourceFile, pos: number): Info | undefined { function getActionForAddMissingDefiniteAssignmentAssertion(context: CodeFixContext, info: Info): CodeFixAction | undefined { if (info.isJs) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => addDefiniteAssignmentAssertion(t, context.sourceFile, info.prop)); + const changes = ChangeTracker.with(context, t => addDefiniteAssignmentAssertion(t, context.sourceFile, info.prop)); return createCodeFixAction(fixName, changes, [Diagnostics.Add_definite_assignment_assertion_to_property_0, info.prop.getText()], fixIdAddDefiniteAssignmentAssertions, Diagnostics.Add_definite_assignment_assertions_to_all_uninitialized_properties); } -function addDefiniteAssignmentAssertion(changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration): void { +function addDefiniteAssignmentAssertion(changeTracker: ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration): void { suppressLeadingAndTrailingTrivia(propertyDeclaration); const property = factory.updatePropertyDeclaration( propertyDeclaration, @@ -114,11 +124,11 @@ function addDefiniteAssignmentAssertion(changeTracker: textChanges.ChangeTracker } function getActionForAddMissingUndefinedType(context: CodeFixContext, info: Info): CodeFixAction { - const changes = textChanges.ChangeTracker.with(context, t => addUndefinedType(t, context.sourceFile, info)); + const changes = ChangeTracker.with(context, t => addUndefinedType(t, context.sourceFile, info)); return createCodeFixAction(fixName, changes, [Diagnostics.Add_undefined_type_to_property_0, info.prop.name.getText()], fixIdAddUndefinedType, Diagnostics.Add_undefined_type_to_all_uninitialized_properties); } -function addUndefinedType(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info): void { +function addUndefinedType(changeTracker: ChangeTracker, sourceFile: SourceFile, info: Info): void { const undefinedTypeNode = factory.createKeywordTypeNode(SyntaxKind.UndefinedKeyword); const types = isUnionTypeNode(info.type) ? info.type.types.concat(undefinedTypeNode) : [info.type, undefinedTypeNode]; const unionTypeNode = factory.createUnionTypeNode(types); @@ -137,11 +147,11 @@ function getActionForAddMissingInitializer(context: CodeFixContext, info: Info): const initializer = getInitializer(checker, info.prop); if (!initializer) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => addInitializer(t, context.sourceFile, info.prop, initializer)); + const changes = ChangeTracker.with(context, t => addInitializer(t, context.sourceFile, info.prop, initializer)); return createCodeFixAction(fixName, changes, [Diagnostics.Add_initializer_to_property_0, info.prop.name.getText()], fixIdAddInitializer, Diagnostics.Add_initializers_to_all_uninitialized_properties); } -function addInitializer(changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration, initializer: Expression): void { +function addInitializer(changeTracker: ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration, initializer: Expression): void { suppressLeadingAndTrailingTrivia(propertyDeclaration); const property = factory.updatePropertyDeclaration( propertyDeclaration, diff --git a/src/services/codefixes/fixUnmatchedParameter.ts b/src/services/codefixes/fixUnmatchedParameter.ts index 267e207a6ccac..6535e65fff5ac 100644 --- a/src/services/codefixes/fixUnmatchedParameter.ts +++ b/src/services/codefixes/fixUnmatchedParameter.ts @@ -1,34 +1,42 @@ import { - __String, append, - CodeFixAction, - CodeFixContext, - Diagnostics, - factory, firstDefined, - getHostSignatureFromJSDoc, - getJSDocHost, - getJSDocTags, - getTokenAtPosition, - HasJSDoc, - Identifier, + length, + map, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isIdentifier, isJSDocParameterTag, +} from "../../compiler/factory/nodeTests"; +import { + __String, + HasJSDoc, + Identifier, JSDocParameterTag, JSDocTag, - length, - map, SignatureDeclaration, SourceFile, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getHostSignatureFromJSDoc, + getJSDocHost, +} from "../../compiler/utilities"; +import { getJSDocTags } from "../../compiler/utilitiesPublic"; import { createCodeFixAction, createCodeFixActionWithoutFixAll, createCombinedCodeActions, eachDiagnostic, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + CodeFixAction, + CodeFixContext, +} from "../types"; +import { getTokenAtPosition } from "../utilities"; const deleteUnmatchedParameter = "deleteUnmatchedParameter"; const renameUnmatchedParameter = "renameUnmatchedParameter"; @@ -53,7 +61,7 @@ registerCodeFix({ }, getAllCodeActions: function getAllCodeActionsToFixUnmatchedParameter(context) { const tagsToSignature = new Map(); - return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => { + return createCombinedCodeActions(ChangeTracker.with(context, changes => { eachDiagnostic(context, errorCodes, ({ file, start }) => { const info = getInfo(file, start); if (info) { @@ -72,7 +80,7 @@ registerCodeFix({ }); function getDeleteAction(context: CodeFixContext, { name, jsDocHost, jsDocParameterTag }: Info) { - const changes = textChanges.ChangeTracker.with(context, changeTracker => + const changes = ChangeTracker.with(context, changeTracker => changeTracker.filterJSDocTags(context.sourceFile, jsDocHost, t => t !== jsDocParameterTag)); return createCodeFixAction( deleteUnmatchedParameter, @@ -109,7 +117,7 @@ function getRenameAction(context: CodeFixContext, { name, jsDocHost, signature, jsDocParameterTag.isNameFirst, jsDocParameterTag.comment ); - const changes = textChanges.ChangeTracker.with(context, changeTracker => + const changes = ChangeTracker.with(context, changeTracker => changeTracker.replaceJSDocComment(sourceFile, jsDocHost, map(tags, t => t === jsDocParameterTag ? newJSDocParameterTag : t))); return createCodeFixActionWithoutFixAll(renameUnmatchedParameter, changes, [Diagnostics.Rename_param_tag_name_0_to_1, name.getText(sourceFile), parameterName]); } diff --git a/src/services/codefixes/fixUnreachableCode.ts b/src/services/codefixes/fixUnreachableCode.ts index 17afabda71a93..f0e3bfae14c44 100644 --- a/src/services/codefixes/fixUnreachableCode.ts +++ b/src/services/codefixes/fixUnreachableCode.ts @@ -1,24 +1,28 @@ import { - Debug, - Diagnostics, emptyArray, - factory, - findAncestor, first, - getTokenAtPosition, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isBlock } from "../../compiler/factory/nodeTests"; +import { IfStatement, - isBlock, - isStatement, - sliceAfter, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { sliceAfter } from "../../compiler/utilities"; +import { + findAncestor, + isStatement, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "fixUnreachableCode"; const errorCodes = [Diagnostics.Unreachable_code_detected.code]; @@ -27,14 +31,14 @@ registerCodeFix({ getCodeActions(context) { const syntacticDiagnostics = context.program.getSyntacticDiagnostics(context.sourceFile, context.cancellationToken); if (syntacticDiagnostics.length) return; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start, context.span.length, context.errorCode)); + const changes = ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start, context.span.length, context.errorCode)); return [createCodeFixAction(fixId, changes, Diagnostics.Remove_unreachable_code, fixId, Diagnostics.Remove_all_unreachable_code)]; }, fixIds: [fixId], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, diag.start, diag.length, diag.code)), }); -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number, length: number, errorCode: number): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, start: number, length: number, errorCode: number): void { const token = getTokenAtPosition(sourceFile, start); const statement = findAncestor(token, isStatement)!; if (statement.getStart(sourceFile) !== token.getStart(sourceFile)) { diff --git a/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts b/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts index ec5b5e6e3ca90..cf7a23dfbc8ab 100644 --- a/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts +++ b/src/services/codefixes/fixUnreferenceableDecoratorMetadata.ts @@ -1,33 +1,39 @@ import { append, - CodeFixAction, - Diagnostics, emptyArray, find, - forEachImportClauseDeclaration, - getTokenAtPosition, - ImportClause, - ImportEqualsDeclaration, - ImportSpecifier, + or, + tryCast, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { isIdentifier, isImportClause, isImportEqualsDeclaration, isImportSpecifier, +} from "../../compiler/factory/nodeTests"; +import { + ImportClause, + ImportEqualsDeclaration, + ImportSpecifier, Node, - or, Program, - refactor, - skipAlias, SourceFile, SymbolFlags, SyntaxKind, - textChanges, - tryCast, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + forEachImportClauseDeclaration, + skipAlias, +} from "../../compiler/utilities"; import { createCodeFixActionWithoutFixAll, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { doChangeNamedToNamespaceOrDefault } from "../refactors/convertImport"; +import { ChangeTracker } from "../textChanges"; +import { CodeFixAction } from "../types"; +import { getTokenAtPosition } from "../utilities"; const fixId = "fixUnreferenceableDecoratorMetadata"; const errorCodes = [Diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_or_a_namespace_import_when_isolatedModules_and_emitDecoratorMetadata_are_enabled.code]; @@ -37,8 +43,8 @@ registerCodeFix({ const importDeclaration = getImportDeclaration(context.sourceFile, context.program, context.span.start); if (!importDeclaration) return; - const namespaceChanges = textChanges.ChangeTracker.with(context, t => importDeclaration.kind === SyntaxKind.ImportSpecifier && doNamespaceImportChange(t, context.sourceFile, importDeclaration, context.program)); - const typeOnlyChanges = textChanges.ChangeTracker.with(context, t => doTypeOnlyImportChange(t, context.sourceFile, importDeclaration, context.program)); + const namespaceChanges = ChangeTracker.with(context, t => importDeclaration.kind === SyntaxKind.ImportSpecifier && doNamespaceImportChange(t, context.sourceFile, importDeclaration, context.program)); + const typeOnlyChanges = ChangeTracker.with(context, t => doTypeOnlyImportChange(t, context.sourceFile, importDeclaration, context.program)); let actions: CodeFixAction[] | undefined; if (namespaceChanges.length) { actions = append(actions, createCodeFixActionWithoutFixAll(fixId, namespaceChanges, Diagnostics.Convert_named_imports_to_namespace_import)); @@ -65,7 +71,7 @@ function getImportDeclaration(sourceFile: SourceFile, program: Program, start: n // cannot be done cleanly, we could offer to *extract* the offending import to a // new type-only import declaration, but honestly I doubt anyone will ever use this // codefix at all, so it's probably not worth the lines of code. -function doTypeOnlyImportChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, importDeclaration: ImportClause | ImportSpecifier | ImportEqualsDeclaration, program: Program) { +function doTypeOnlyImportChange(changes: ChangeTracker, sourceFile: SourceFile, importDeclaration: ImportClause | ImportSpecifier | ImportEqualsDeclaration, program: Program) { if (importDeclaration.kind === SyntaxKind.ImportEqualsDeclaration) { changes.insertModifierBefore(sourceFile, SyntaxKind.TypeKeyword, importDeclaration.name); return; @@ -93,6 +99,6 @@ function doTypeOnlyImportChange(changes: textChanges.ChangeTracker, sourceFile: changes.insertModifierBefore(sourceFile, SyntaxKind.TypeKeyword, importClause); } -function doNamespaceImportChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, importDeclaration: ImportSpecifier, program: Program) { - refactor.doChangeNamedToNamespaceOrDefault(sourceFile, program, changes, importDeclaration.parent); +function doNamespaceImportChange(changes: ChangeTracker, sourceFile: SourceFile, importDeclaration: ImportSpecifier, program: Program) { + doChangeNamedToNamespaceOrDefault(sourceFile, program, changes, importDeclaration.parent); } diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index 4da1d6ce22d9c..b03089b4ad953 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -1,31 +1,19 @@ import { - ArrayBindingPattern, - CancellationToken, cast, - CodeFixAction, - CodeFixContext, - Debug, - DiagnosticMessage, - DiagnosticOrDiagnosticAndArguments, - Diagnostics, - factory, - FileTextChanges, - FindAllReferences, first, forEach, - FunctionLikeDeclaration, - getJSDocParameterTags, - getNewLineOrDefaultFromHost, - getPrecedingNonSpaceCharacterPosition, - getTokenAtPosition, - Identifier, - ImportDeclaration, + length, + map, + tryCast, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrayBindingPattern, isBinaryExpression, isCallExpression, - isCallLikeExpression, isComputedPropertyName, - isDeclarationWithTypeParameterChildren, isExpressionStatement, isIdentifier, isImportClause, @@ -34,7 +22,6 @@ import { isJSDocTemplateTag, isMethodDeclaration, isMethodSignature, - isModifier, isObjectBindingPattern, isParameter, isPostfixUnaryExpression, @@ -43,27 +30,53 @@ import { isSuperKeyword, isVariableDeclaration, isVariableDeclarationList, - length, - map, +} from "../../compiler/factory/nodeTests"; +import { + ArrayBindingPattern, + CancellationToken, + DiagnosticMessage, + FunctionLikeDeclaration, + Identifier, + ImportDeclaration, Node, ObjectBindingPattern, ParameterDeclaration, - probablyUsesSemicolons, Program, - showModuleSpecifier, SourceFile, SyntaxKind, - textChanges, - tryCast, TypeChecker, VariableDeclaration, VariableDeclarationList, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + isDeclarationWithTypeParameterChildren, + showModuleSpecifier, +} from "../../compiler/utilities"; +import { + getJSDocParameterTags, + isCallLikeExpression, + isModifier, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { Core as FindAllReferences } from "../findAllReferences"; +import { ChangeTracker } from "../textChanges"; +import { + CodeFixAction, + CodeFixContext, + EntryKind, + FileTextChanges, +} from "../types"; +import { + DiagnosticOrDiagnosticAndArguments, + getNewLineOrDefaultFromHost, + getPrecedingNonSpaceCharacterPosition, + getTokenAtPosition, + probablyUsesSemicolons, +} from "../utilities"; const fixName = "unusedIdentifier"; const fixIdPrefix = "unusedIdentifier_prefix"; @@ -89,19 +102,19 @@ registerCodeFix({ const token = getTokenAtPosition(sourceFile, context.span.start); if (isJSDocTemplateTag(token)) { - return [createDeleteFix(textChanges.ChangeTracker.with(context, t => t.delete(sourceFile, token)), Diagnostics.Remove_template_tag)]; + return [createDeleteFix(ChangeTracker.with(context, t => t.delete(sourceFile, token)), Diagnostics.Remove_template_tag)]; } if (token.kind === SyntaxKind.LessThanToken) { - const changes = textChanges.ChangeTracker.with(context, t => deleteTypeParameters(t, sourceFile, token)); + const changes = ChangeTracker.with(context, t => deleteTypeParameters(t, sourceFile, token)); return [createDeleteFix(changes, Diagnostics.Remove_type_parameters)]; } const importDecl = tryGetFullImport(token); if (importDecl) { - const changes = textChanges.ChangeTracker.with(context, t => t.delete(sourceFile, importDecl)); + const changes = ChangeTracker.with(context, t => t.delete(sourceFile, importDecl)); return [createCodeFixAction(fixName, changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)], fixIdDeleteImports, Diagnostics.Delete_all_unused_imports)]; } else if (isImport(token)) { - const deletion = textChanges.ChangeTracker.with(context, t => tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, program, cancellationToken, /*isFixAll*/ false)); + const deletion = ChangeTracker.with(context, t => tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, program, cancellationToken, /*isFixAll*/ false)); if (deletion.length) { return [createCodeFixAction(fixName, deletion, [Diagnostics.Remove_unused_declaration_for_Colon_0, token.getText(sourceFile)], fixIdDeleteImports, Diagnostics.Delete_all_unused_imports)]; } @@ -115,31 +128,31 @@ registerCodeFix({ map(elements, e => e.getText(sourceFile)).join(", ") ]; return [ - createDeleteFix(textChanges.ChangeTracker.with(context, t => + createDeleteFix(ChangeTracker.with(context, t => deleteDestructuringElements(t, sourceFile, token.parent as ObjectBindingPattern | ArrayBindingPattern)), diagnostic) ]; } return [ - createDeleteFix(textChanges.ChangeTracker.with(context, t => + createDeleteFix(ChangeTracker.with(context, t => deleteDestructuring(context, t, sourceFile, token.parent as ObjectBindingPattern | ArrayBindingPattern)), Diagnostics.Remove_unused_destructuring_declaration), ]; } if (canDeleteEntireVariableStatement(sourceFile, token)) { return [ - createDeleteFix(textChanges.ChangeTracker.with(context, t => + createDeleteFix(ChangeTracker.with(context, t => deleteEntireVariableStatement(t, sourceFile, token.parent as VariableDeclarationList)), Diagnostics.Remove_variable_statement) ]; } const result: CodeFixAction[] = []; if (token.kind === SyntaxKind.InferKeyword) { - const changes = textChanges.ChangeTracker.with(context, t => changeInferToUnknown(t, sourceFile, token)); + const changes = ChangeTracker.with(context, t => changeInferToUnknown(t, sourceFile, token)); const name = cast(token.parent, isInferTypeNode).typeParameter.name.text; result.push(createCodeFixAction(fixName, changes, [Diagnostics.Replace_infer_0_with_unknown, name], fixIdInfer, Diagnostics.Replace_all_unused_infer_with_unknown)); } else { - const deletion = textChanges.ChangeTracker.with(context, t => + const deletion = ChangeTracker.with(context, t => tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, program, cancellationToken, /*isFixAll*/ false)); if (deletion.length) { const name = isComputedPropertyName(token.parent) ? token.parent : token; @@ -147,7 +160,7 @@ registerCodeFix({ } } - const prefix = textChanges.ChangeTracker.with(context, t => tryPrefixDeclaration(t, errorCode, sourceFile, token)); + const prefix = ChangeTracker.with(context, t => tryPrefixDeclaration(t, errorCode, sourceFile, token)); if (prefix.length) { result.push(createCodeFixAction(fixName, prefix, [Diagnostics.Prefix_0_with_an_underscore, token.getText(sourceFile)], fixIdPrefix, Diagnostics.Prefix_all_unused_declarations_with_where_possible)); } @@ -216,7 +229,7 @@ registerCodeFix({ }, }); -function changeInferToUnknown(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node): void { +function changeInferToUnknown(changes: ChangeTracker, sourceFile: SourceFile, token: Node): void { changes.replaceNode(sourceFile, token.parent, factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword)); } @@ -224,7 +237,7 @@ function createDeleteFix(changes: FileTextChanges[], diag: DiagnosticOrDiagnosti return createCodeFixAction(fixName, changes, diag, fixIdDelete, Diagnostics.Delete_all_unused_declarations); } -function deleteTypeParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node): void { +function deleteTypeParameters(changes: ChangeTracker, sourceFile: SourceFile, token: Node): void { changes.delete(sourceFile, Debug.checkDefined(cast(token.parent, isDeclarationWithTypeParameterChildren).typeParameters, "The type parameter to delete should exist")); } @@ -242,15 +255,15 @@ function canDeleteEntireVariableStatement(sourceFile: SourceFile, token: Node): return isVariableDeclarationList(token.parent) && first(token.parent.getChildren(sourceFile)) === token; } -function deleteEntireVariableStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: VariableDeclarationList) { +function deleteEntireVariableStatement(changes: ChangeTracker, sourceFile: SourceFile, node: VariableDeclarationList) { changes.delete(sourceFile, node.parent.kind === SyntaxKind.VariableStatement ? node.parent : node); } -function deleteDestructuringElements(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: ObjectBindingPattern | ArrayBindingPattern) { +function deleteDestructuringElements(changes: ChangeTracker, sourceFile: SourceFile, node: ObjectBindingPattern | ArrayBindingPattern) { forEach(node.elements, n => changes.delete(sourceFile, n)); } -function deleteDestructuring(context: CodeFixContext, changes: textChanges.ChangeTracker, sourceFile: SourceFile, { parent }: ObjectBindingPattern | ArrayBindingPattern) { +function deleteDestructuring(context: CodeFixContext, changes: ChangeTracker, sourceFile: SourceFile, { parent }: ObjectBindingPattern | ArrayBindingPattern) { if (isVariableDeclaration(parent) && parent.initializer && isCallLikeExpression(parent.initializer)) { if (isVariableDeclarationList(parent.parent) && length(parent.parent.declarations) > 1) { const varStatement = parent.parent.parent; @@ -271,7 +284,7 @@ function deleteDestructuring(context: CodeFixContext, changes: textChanges.Chang } } -function tryPrefixDeclaration(changes: textChanges.ChangeTracker, errorCode: number, sourceFile: SourceFile, token: Node): void { +function tryPrefixDeclaration(changes: ChangeTracker, errorCode: number, sourceFile: SourceFile, token: Node): void { // Don't offer to prefix a property. if (errorCode === Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code) return; if (token.kind === SyntaxKind.InferKeyword) { @@ -306,10 +319,10 @@ function canPrefix(token: Identifier): boolean { return false; } -function tryDeleteDeclaration(sourceFile: SourceFile, token: Node, changes: textChanges.ChangeTracker, checker: TypeChecker, sourceFiles: readonly SourceFile[], program: Program, cancellationToken: CancellationToken, isFixAll: boolean) { +function tryDeleteDeclaration(sourceFile: SourceFile, token: Node, changes: ChangeTracker, checker: TypeChecker, sourceFiles: readonly SourceFile[], program: Program, cancellationToken: CancellationToken, isFixAll: boolean) { tryDeleteDeclarationWorker(token, changes, sourceFile, checker, sourceFiles, program, cancellationToken, isFixAll); if (isIdentifier(token)) { - FindAllReferences.Core.eachSymbolReferenceInFile(token, checker, sourceFile, (ref: Node) => { + FindAllReferences.eachSymbolReferenceInFile(token, checker, sourceFile, (ref: Node) => { if (isPropertyAccessExpression(ref.parent) && ref.parent.name === ref) ref = ref.parent; if (!isFixAll && mayDeleteExpression(ref)) { changes.delete(sourceFile, ref.parent.parent); @@ -318,12 +331,12 @@ function tryDeleteDeclaration(sourceFile: SourceFile, token: Node, changes: text } } -function tryDeleteDeclarationWorker(token: Node, changes: textChanges.ChangeTracker, sourceFile: SourceFile, checker: TypeChecker, sourceFiles: readonly SourceFile[], program: Program, cancellationToken: CancellationToken, isFixAll: boolean): void { +function tryDeleteDeclarationWorker(token: Node, changes: ChangeTracker, sourceFile: SourceFile, checker: TypeChecker, sourceFiles: readonly SourceFile[], program: Program, cancellationToken: CancellationToken, isFixAll: boolean): void { const { parent } = token; if (isParameter(parent)) { tryDeleteParameter(changes, sourceFile, parent, checker, sourceFiles, program, cancellationToken, isFixAll); } - else if (!(isFixAll && isIdentifier(token) && FindAllReferences.Core.isSymbolReferencedInFile(token, checker, sourceFile))) { + else if (!(isFixAll && isIdentifier(token) && FindAllReferences.isSymbolReferencedInFile(token, checker, sourceFile))) { const node = isImportClause(parent) ? token : isComputedPropertyName(parent) ? parent.parent : parent; Debug.assert(node !== sourceFile, "should not delete whole source file"); changes.delete(sourceFile, node); @@ -331,7 +344,7 @@ function tryDeleteDeclarationWorker(token: Node, changes: textChanges.ChangeTrac } function tryDeleteParameter( - changes: textChanges.ChangeTracker, + changes: ChangeTracker, sourceFile: SourceFile, parameter: ParameterDeclaration, checker: TypeChecker, @@ -341,7 +354,7 @@ function tryDeleteParameter( isFixAll = false): void { if (mayDeleteParameter(checker, sourceFile, parameter, sourceFiles, program, cancellationToken, isFixAll)) { if (parameter.modifiers && parameter.modifiers.length > 0 && - (!isIdentifier(parameter.name) || FindAllReferences.Core.isSymbolReferencedInFile(parameter.name, checker, sourceFile))) { + (!isIdentifier(parameter.name) || FindAllReferences.isSymbolReferencedInFile(parameter.name, checker, sourceFile))) { for (const modifier of parameter.modifiers) { if (isModifier(modifier)) { changes.deleteModifier(sourceFile, modifier); @@ -357,7 +370,7 @@ function tryDeleteParameter( function isNotProvidedArguments(parameter: ParameterDeclaration, checker: TypeChecker, sourceFiles: readonly SourceFile[]) { const index = parameter.parent.parameters.indexOf(parameter); // Just in case the call didn't provide enough arguments. - return !FindAllReferences.Core.someSignatureUsage(parameter.parent, sourceFiles, checker, (_, call) => !call || call.arguments.length > index); + return !FindAllReferences.someSignatureUsage(parameter.parent, sourceFiles, checker, (_, call) => !call || call.arguments.length > index); } function mayDeleteParameter(checker: TypeChecker, sourceFile: SourceFile, parameter: ParameterDeclaration, sourceFiles: readonly SourceFile[], program: Program, cancellationToken: CancellationToken, isFixAll: boolean): boolean { @@ -367,11 +380,11 @@ function mayDeleteParameter(checker: TypeChecker, sourceFile: SourceFile, parame case SyntaxKind.Constructor: const index = parent.parameters.indexOf(parameter); const referent = isMethodDeclaration(parent) ? parent.name : parent; - const entries = FindAllReferences.Core.getReferencedSymbolsForNode(parent.pos, referent, program, sourceFiles, cancellationToken); + const entries = FindAllReferences.getReferencedSymbolsForNode(parent.pos, referent, program, sourceFiles, cancellationToken); if (entries) { for (const entry of entries) { for (const reference of entry.references) { - if (reference.kind === FindAllReferences.EntryKind.Node) { + if (reference.kind === EntryKind.Node) { // argument in super(...) const isSuperCall = isSuperKeyword(reference.node) && isCallExpression(reference.node.parent) @@ -416,7 +429,7 @@ function mayDeleteParameter(checker: TypeChecker, sourceFile: SourceFile, parame } function isCallbackLike(checker: TypeChecker, sourceFile: SourceFile, name: Identifier): boolean { - return !!FindAllReferences.Core.eachSymbolReferenceInFile(name, checker, sourceFile, reference => + return !!FindAllReferences.eachSymbolReferenceInFile(name, checker, sourceFile, reference => isIdentifier(reference) && isCallExpression(reference.parent) && reference.parent.arguments.indexOf(reference) >= 0); } diff --git a/src/services/codefixes/fixUnusedLabel.ts b/src/services/codefixes/fixUnusedLabel.ts index 88e6aed5d41a6..d49b68b66e0ea 100644 --- a/src/services/codefixes/fixUnusedLabel.ts +++ b/src/services/codefixes/fixUnusedLabel.ts @@ -1,34 +1,36 @@ +import { cast } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { isLabeledStatement } from "../../compiler/factory/nodeTests"; +import { skipTrivia } from "../../compiler/scanner"; import { - cast, - Diagnostics, - findChildOfKind, - getTokenAtPosition, - isLabeledStatement, - positionsAreOnSameLine, - skipTrivia, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { positionsAreOnSameLine } from "../../compiler/utilities"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + findChildOfKind, + getTokenAtPosition, +} from "../utilities"; const fixId = "fixUnusedLabel"; const errorCodes = [Diagnostics.Unused_label.code]; registerCodeFix({ errorCodes, getCodeActions(context) { - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start)); + const changes = ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start)); return [createCodeFixAction(fixId, changes, Diagnostics.Remove_unused_label, fixId, Diagnostics.Remove_all_unused_labels)]; }, fixIds: [fixId], getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, diag.start)), }); -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, start: number): void { const token = getTokenAtPosition(sourceFile, start); const labeledStatement = cast(token.parent, isLabeledStatement); const pos = token.getStart(sourceFile); diff --git a/src/services/codefixes/generateAccessors.ts b/src/services/codefixes/generateAccessors.ts index 772abe45614af..ea729ff43369b 100644 --- a/src/services/codefixes/generateAccessors.ts +++ b/src/services/codefixes/generateAccessors.ts @@ -1,61 +1,75 @@ import { - AccessorDeclaration, - canHaveDecorators, cast, - ClassLikeDeclaration, concatenate, - ConstructorDeclaration, - DeclarationName, - Diagnostics, - factory, - FileTextChanges, find, - findAncestor, - getClassExtendsHeritageElement, - getDecorators, - getEffectiveModifierFlags, - getFirstConstructorWithBody, - getLocaleSpecificMessage, - getTokenAtPosition, - getTypeAnnotationNode, - getUniqueName, - hasEffectiveReadonlyModifier, - hasStaticModifier, - Identifier, - InterfaceDeclaration, - isClassLike, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isElementAccessExpression, - isFunctionLike, isIdentifier, - isParameterPropertyDeclaration, isPropertyAccessExpression, isPropertyAssignment, isPropertyDeclaration, - isSourceFileJS, isStringLiteral, isUnionTypeNode, - isWriteAccess, +} from "../../compiler/factory/nodeTests"; +import { canHaveDecorators } from "../../compiler/factory/utilitiesPublic"; +import { + AccessorDeclaration, + ClassLikeDeclaration, + ConstructorDeclaration, + DeclarationName, + Identifier, + InterfaceDeclaration, ModifierFlags, ModifierLike, Mutable, Node, - nodeOverlapsWithStartEnd, ObjectLiteralExpression, - ParameterPropertyDeclaration, Program, PropertyAssignment, PropertyDeclaration, - refactor, SourceFile, - startsWithUnderscore, StringLiteral, - suppressLeadingAndTrailingTrivia, SymbolFlags, SyntaxKind, - textChanges, TypeChecker, TypeNode, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getClassExtendsHeritageElement, + getEffectiveModifierFlags, + getFirstConstructorWithBody, + getLocaleSpecificMessage, + getTypeAnnotationNode, + hasEffectiveReadonlyModifier, + hasStaticModifier, + isSourceFileJS, + isWriteAccess, +} from "../../compiler/utilities"; +import { + findAncestor, + getDecorators, + isClassLike, + isFunctionLike, + isParameterPropertyDeclaration, + ParameterPropertyDeclaration, +} from "../../compiler/utilitiesPublic"; +import { isRefactorErrorInfo } from "../refactors/helpers"; +import { ChangeTracker } from "../textChanges"; +import { + FileTextChanges, + RefactorErrorInfo, + TextChangesContext, +} from "../types"; +import { + getTokenAtPosition, + getUniqueName, + nodeOverlapsWithStartEnd, + startsWithUnderscore, + suppressLeadingAndTrailingTrivia, +} from "../utilities"; /** @internal */ export type AcceptedDeclaration = ParameterPropertyDeclaration | PropertyDeclaration | PropertyAssignment; @@ -65,7 +79,7 @@ export type AcceptedNameType = Identifier | StringLiteral; export type ContainerDeclaration = ClassLikeDeclaration | ObjectLiteralExpression; /** @internal */ -export type AccessorOrRefactorErrorInfo = AccessorInfo | refactor.RefactorErrorInfo; +export type AccessorOrRefactorErrorInfo = AccessorInfo | RefactorErrorInfo; /** @internal */ export interface AccessorInfo { readonly container: ContainerDeclaration; @@ -80,11 +94,11 @@ export interface AccessorInfo { } /** @internal */ -export function generateAccessorFromProperty(file: SourceFile, program: Program, start: number, end: number, context: textChanges.TextChangesContext, _actionName: string): FileTextChanges[] | undefined { +export function generateAccessorFromProperty(file: SourceFile, program: Program, start: number, end: number, context: TextChangesContext, _actionName: string): FileTextChanges[] | undefined { const fieldInfo = getAccessorConvertiblePropertyAtPosition(file, program, start, end); - if (!fieldInfo || refactor.isRefactorErrorInfo(fieldInfo)) return undefined; + if (!fieldInfo || isRefactorErrorInfo(fieldInfo)) return undefined; - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = ChangeTracker.fromContext(context); const { isStatic, isReadonly, fieldName, accessorName, originalName, type, container, declaration } = fieldInfo; suppressLeadingAndTrailingTrivia(fieldName); @@ -246,7 +260,7 @@ function generateSetAccessor(fieldName: AcceptedNameType, accessorName: Accepted ); } -function updatePropertyDeclaration(changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: PropertyDeclaration, type: TypeNode | undefined, fieldName: AcceptedNameType, modifiers: readonly ModifierLike[] | undefined) { +function updatePropertyDeclaration(changeTracker: ChangeTracker, file: SourceFile, declaration: PropertyDeclaration, type: TypeNode | undefined, fieldName: AcceptedNameType, modifiers: readonly ModifierLike[] | undefined) { const property = factory.updatePropertyDeclaration( declaration, modifiers, @@ -258,7 +272,7 @@ function updatePropertyDeclaration(changeTracker: textChanges.ChangeTracker, fil changeTracker.replaceNode(file, declaration, property); } -function updatePropertyAssignmentDeclaration(changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: PropertyAssignment, fieldName: AcceptedNameType) { +function updatePropertyAssignmentDeclaration(changeTracker: ChangeTracker, file: SourceFile, declaration: PropertyAssignment, fieldName: AcceptedNameType) { let assignment = factory.updatePropertyAssignment(declaration, fieldName, declaration.initializer); // Remove grammar errors from assignment if (assignment.modifiers || assignment.questionToken || assignment.exclamationToken) { @@ -270,7 +284,7 @@ function updatePropertyAssignmentDeclaration(changeTracker: textChanges.ChangeTr changeTracker.replacePropertyAssignment(file, declaration, assignment); } -function updateFieldDeclaration(changeTracker: textChanges.ChangeTracker, file: SourceFile, declaration: AcceptedDeclaration, type: TypeNode | undefined, fieldName: AcceptedNameType, modifiers: readonly ModifierLike[] | undefined) { +function updateFieldDeclaration(changeTracker: ChangeTracker, file: SourceFile, declaration: AcceptedDeclaration, type: TypeNode | undefined, fieldName: AcceptedNameType, modifiers: readonly ModifierLike[] | undefined) { if (isPropertyDeclaration(declaration)) { updatePropertyDeclaration(changeTracker, file, declaration, type, fieldName, modifiers); } @@ -283,13 +297,13 @@ function updateFieldDeclaration(changeTracker: textChanges.ChangeTracker, file: } } -function insertAccessor(changeTracker: textChanges.ChangeTracker, file: SourceFile, accessor: AccessorDeclaration, declaration: AcceptedDeclaration, container: ContainerDeclaration) { +function insertAccessor(changeTracker: ChangeTracker, file: SourceFile, accessor: AccessorDeclaration, declaration: AcceptedDeclaration, container: ContainerDeclaration) { isParameterPropertyDeclaration(declaration, declaration.parent) ? changeTracker.insertMemberAtStart(file, container as ClassLikeDeclaration, accessor) : isPropertyAssignment(declaration) ? changeTracker.insertNodeAfterComma(file, declaration, accessor) : changeTracker.insertNodeAfter(file, declaration, accessor); } -function updateReadonlyPropertyInitializerStatementConstructor(changeTracker: textChanges.ChangeTracker, file: SourceFile, constructor: ConstructorDeclaration, fieldName: string, originalName: string) { +function updateReadonlyPropertyInitializerStatementConstructor(changeTracker: ChangeTracker, file: SourceFile, constructor: ConstructorDeclaration, fieldName: string, originalName: string) { if (!constructor.body) return; constructor.body.forEachChild(function recur(node) { if (isElementAccessExpression(node) && diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index e47e4c99777a2..0701e57a0f0b0 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -1,61 +1,50 @@ import { - AccessorDeclaration, append, arrayFrom, - ArrowFunction, - Block, - CallExpression, - CharacterCodes, - ClassLikeDeclaration, - CodeFixContextBase, combine, - Debug, - Diagnostics, emptyArray, - EntityName, - Expression, - factory, find, flatMap, - FunctionDeclaration, - FunctionExpression, - GetAccessorDeclaration, - getAllAccessorDeclarations, - getEffectiveModifierFlags, - getEmitScriptTarget, - getFirstIdentifier, - getModuleSpecifierResolverHost, - getNameForExportedSymbol, - getNameOfDeclaration, - getQuotePreference, - getSetAccessorValueParameter, - getSynthesizedDeepClone, - getTokenAtPosition, - getTsConfigObjectLiteralExpression, - hasAbstractModifier, - Identifier, - idText, - IntersectionType, + length, + map, + sameMap, + some, + tryCast, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, - isAutoAccessorPropertyDeclaration, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isIdentifier, isImportTypeNode, - isInJSFile, - isLiteralImportTypeNode, isMethodDeclaration, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSetAccessorDeclaration, isStringLiteral, - isTypeNode, isYieldExpression, - LanguageServiceHost, - length, - map, +} from "../../compiler/factory/nodeTests"; +import { setTextRange } from "../../compiler/factory/utilitiesPublic"; +import { nullTransformationContext } from "../../compiler/transformer"; +import { + AccessorDeclaration, + ArrowFunction, + Block, + CallExpression, + CharacterCodes, + ClassLikeDeclaration, + EntityName, + Expression, + FunctionDeclaration, + FunctionExpression, + GetAccessorDeclaration, + Identifier, + IntersectionType, MethodDeclaration, MethodSignature, Modifier, @@ -64,7 +53,6 @@ import { NodeArray, NodeBuilderFlags, NodeFlags, - nullTransformationContext, ObjectFlags, ObjectLiteralExpression, ObjectType, @@ -73,24 +61,16 @@ import { PropertyAssignment, PropertyDeclaration, PropertyName, - QuotePreference, - sameMap, ScriptTarget, SetAccessorDeclaration, - setTextRange, Signature, SignatureDeclaration, - signatureHasRestParameter, - some, SourceFile, Symbol, SymbolFlags, SymbolTracker, SyntaxKind, - textChanges, TextSpan, - textSpanEnd, - tryCast, TsConfigSourceFile, Type, TypeChecker, @@ -99,11 +79,45 @@ import { TypeParameterDeclaration, UnionType, UserPreferences, +} from "../../compiler/types"; +import { + getAllAccessorDeclarations, + getEffectiveModifierFlags, + getEmitScriptTarget, + getFirstIdentifier, + getSetAccessorValueParameter, + getTsConfigObjectLiteralExpression, + hasAbstractModifier, + isInJSFile, + isLiteralImportTypeNode, + signatureHasRestParameter, +} from "../../compiler/utilities"; +import { + getNameOfDeclaration, + idText, + isAutoAccessorPropertyDeclaration, + isTypeNode, + textSpanEnd, +} from "../../compiler/utilitiesPublic"; +import { visitEachChild, visitNode, visitNodes, -} from "../_namespaces/ts"; -import { ImportAdder } from "../_namespaces/ts.codefix"; +} from "../../compiler/visitorPublic"; +import { ChangeTracker } from "../textChanges"; +import { + CodeFixContextBase, + LanguageServiceHost, +} from "../types"; +import { + getModuleSpecifierResolverHost, + getNameForExportedSymbol, + getQuotePreference, + getSynthesizedDeepClone, + getTokenAtPosition, + QuotePreference, +} from "../utilities"; +import { ImportAdder } from "./importAdder"; /** * Finds members of the resolved type that are missing in the class pointed to by class decl @@ -794,7 +808,7 @@ export function createStubbedBody(text: string, quotePreference: QuotePreference /** @internal */ export function setJsonCompilerOptionValues( - changeTracker: textChanges.ChangeTracker, + changeTracker: ChangeTracker, configFile: TsConfigSourceFile, options: [string, Expression][] ) { @@ -827,7 +841,7 @@ export function setJsonCompilerOptionValues( /** @internal */ export function setJsonCompilerOptionValue( - changeTracker: textChanges.ChangeTracker, + changeTracker: ChangeTracker, configFile: TsConfigSourceFile, optionName: string, optionValue: Expression, diff --git a/src/services/codefixes/importAdder.ts b/src/services/codefixes/importAdder.ts new file mode 100644 index 0000000000000..e6c041d08e0b3 --- /dev/null +++ b/src/services/codefixes/importAdder.ts @@ -0,0 +1,1645 @@ +import { + arrayFrom, + cast, + combine, + compareBooleans, + compareValues, + createMultiMap, + emptyArray, + every, + first, + firstDefined, + flatMap, + flatMapIterator, + last, + mapDefined, + memoizeOne, + MultiMap, + removeSuffix, + single, + some, + sort, + SortKind, + stableSort, + startsWith, + tryCast, +} from "../../compiler/core"; +import { Comparison } from "../../compiler/corePublic"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { removeFileExtension } from "../../compiler/extension"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { + isExternalModuleReference, + isIdentifier, + isImportEqualsDeclaration, + isJsxClosingElement, + isJsxOpeningFragment, + isNamedImports, + isNamespaceImport, + isStringLiteral, +} from "../../compiler/factory/nodeTests"; +import { pathContainsNodeModules } from "../../compiler/moduleNameResolver"; +import { + getModuleSpecifiersWithCacheInfo, + tryGetModuleSpecifiersFromCache, +} from "../../compiler/moduleSpecifiers"; +import { + getBaseFileName, + getDirectoryPath, + pathIsBareSpecifier, + toPath, +} from "../../compiler/path"; +import { + isIdentifierPart, + isIdentifierStart, +} from "../../compiler/scanner"; +import { + AnyImportOrRequire, + AnyImportOrRequireStatement, + AnyImportSyntax, + CancellationToken, + CompilerOptions, + DiagnosticWithLocation, + Identifier, + ImportClause, + ImportEqualsDeclaration, + ImportsNotUsedAsValues, + InternalSymbolName, + ModuleKind, + ModuleResolutionKind, + Mutable, + NamedImports, + Node, + NodeFlags, + ObjectBindingPattern, + Path, + Program, + RequireVariableStatement, + ScriptTarget, + SourceFile, + StringLiteral, + Symbol, + SymbolFlags, + SymbolId, + SyntaxKind, + TypeChecker, + TypeOnlyAliasDeclaration, + UserPreferences, +} from "../../compiler/types"; +import { + compareNumberOfDirectorySeparators, + getAllowSyntheticDefaultImports, + getEmitModuleKind, + getEmitModuleResolutionKind, + getEmitScriptTarget, + getNodeId, + getSourceFileOfNode, + getSymbolId, + hostGetCanonicalFileName, + importFromModuleSpecifier, + importNameElisionDisabled, + isExternalModule, + isInJSFile, + isIntrinsicJsxName, + isJSXTagName, + isSourceFileJS, + isStringANonContextualKeyword, + isUMDExportSymbol, + isValidTypeOnlyAliasUseSite, + isVariableDeclarationInitializedToRequire, + nodeIsMissing, + skipAlias, + stripQuotes, + tryGetModuleSpecifierFromDeclaration, +} from "../../compiler/utilities"; +import { + isJsxOpeningLikeElement, + isStringLiteralLike, + isTypeOnlyImportDeclaration, + isTypeOnlyImportOrExportDeclaration, +} from "../../compiler/utilitiesPublic"; +import { createCodeFixAction } from "../codeFixProvider"; +import { + forEachExternalModuleToImportFrom, + getDefaultExportInfoWorker, + getDefaultLikeExportInfo, + getExportInfoMap, + ImportKind, + isImportableFile, +} from "../exportInfoMap"; +import { + compareImportOrExportSpecifiers, + detectImportSpecifierSorting, + detectSorting, + getImportSpecifierInsertionIndex, + getOrganizeImportsComparer, +} from "../organizeImports"; +import { ChangeTracker } from "../textChanges"; +import { + CodeAction, + CodeFixAction, + CodeFixContextBase, + ExportKind, + FormatContext, + LanguageServiceHost, + SymbolExportInfo, + TextChangesContext, +} from "../types"; +import { + createModuleSpecifierResolutionHost, + createPackageJsonImportFilter, + DiagnosticOrDiagnosticAndArguments, + getMeaningFromDeclaration, + getMeaningFromLocation, + getNameForExportedSymbol, + getQuoteFromPreference, + getQuotePreference, + getTokenAtPosition, + getTypeKeywordOfTypeOnlyImport, + getUniqueSymbolId, + insertImports, + jsxModeNeedsExplicitImport, + makeImport, + makeStringLiteral, + moduleResolutionUsesNodeModules, + PackageJsonImportFilter, + QuotePreference, + SemanticMeaning, + shouldUseUriStyleNodeCoreModules, +} from "../utilities"; + +/** @internal */ +export const fixName = "import"; + +/** @internal */ +export const fixId = "fixMissingImport"; + +/** + * Computes multiple import additions to a file and writes them to a ChangeTracker. + * + * @internal + */ +export interface ImportAdder { + hasFixes(): boolean; + addImportFromDiagnostic: (diagnostic: DiagnosticWithLocation, context: CodeFixContextBase) => void; + addImportFromExportedSymbol: (exportedSymbol: Symbol, isValidTypeOnlyUseSite?: boolean) => void; + writeFixes: (changeTracker: ChangeTracker) => void; +} + +/** + * Computes module specifiers for multiple import additions to a file. + * + * @internal + */ + export interface ImportSpecifierResolver { + getModuleSpecifierForBestExportInfo( + exportInfo: readonly SymbolExportInfo[], + position: number, + isValidTypeOnlyUseSite: boolean, + fromCacheOnly?: boolean + ): { exportInfo?: SymbolExportInfo, moduleSpecifier: string, computedWithoutCacheCount: number } | undefined; +} + +/** @internal */ +export type ImportFix = FixUseNamespaceImport | FixAddJsdocTypeImport | FixAddToExistingImport | FixAddNewImport | FixPromoteTypeOnlyImport; +type ImportFixWithModuleSpecifier = FixUseNamespaceImport | FixAddJsdocTypeImport | FixAddToExistingImport | FixAddNewImport; + +interface Import { + readonly name: string; + readonly addAsTypeOnly: AddAsTypeOnly; +} + +interface ImportsCollection { + readonly defaultImport?: Import; + readonly namedImports?: Map; + readonly namespaceLikeImport?: { + readonly importKind: ImportKind.CommonJS | ImportKind.Namespace; + readonly name: string; + readonly addAsTypeOnly: AddAsTypeOnly; + }; +} + +interface AddToExistingState { + readonly importClauseOrBindingPattern: ImportClause | ObjectBindingPattern; + defaultImport: Import | undefined; + readonly namedImports: Map; +} + +/** + * Sorted with the preferred fix coming first. + * @internal + */ +export const enum ImportFixKind { UseNamespace, JsdocTypeImport, AddToExisting, AddNew, PromoteTypeOnly } + +/** + * These should not be combined as bitflags, but are given powers of 2 values to + * easily detect conflicts between `NotAllowed` and `Required` by giving them a unique sum. + * They're also ordered in terms of increasing priority for a fix-all scenario (see `reduceAddAsTypeOnlyValues`). + * @internal + */ +export const enum AddAsTypeOnly { + Allowed = 1 << 0, + Required = 1 << 1, + NotAllowed = 1 << 2, +} + +/** @internal */ +export interface ImportFixBase { + readonly isReExport?: boolean; + readonly exportInfo?: SymbolExportInfo; + readonly moduleSpecifier: string; +} + +/** @internal */ +export interface Qualification { + readonly usagePosition: number; + readonly namespacePrefix: string; +} + +/** @internal */ +export interface FixUseNamespaceImport extends ImportFixBase, Qualification { + readonly kind: ImportFixKind.UseNamespace; +} + +/** @internal */ +export interface FixAddJsdocTypeImport extends ImportFixBase { + readonly kind: ImportFixKind.JsdocTypeImport; + readonly usagePosition: number; + readonly isReExport: boolean; + readonly exportInfo: SymbolExportInfo; +} + +/** @internal */ +export interface FixAddToExistingImport extends ImportFixBase { + readonly kind: ImportFixKind.AddToExisting; + readonly importClauseOrBindingPattern: ImportClause | ObjectBindingPattern; + readonly importKind: ImportKind.Default | ImportKind.Named; + readonly addAsTypeOnly: AddAsTypeOnly; +} + +/** @internal */ +export interface FixAddNewImport extends ImportFixBase { + readonly kind: ImportFixKind.AddNew; + readonly importKind: ImportKind; + readonly addAsTypeOnly: AddAsTypeOnly; + readonly useRequire: boolean; + readonly qualification?: Qualification; +} + +/** @internal */ +export interface FixPromoteTypeOnlyImport { + readonly kind: ImportFixKind.PromoteTypeOnly; + readonly typeOnlyAliasDeclaration: TypeOnlyAliasDeclaration; +} + +/** @internal */ +export interface FixAddToExistingImportInfo { + readonly declaration: AnyImportOrRequire; + readonly importKind: ImportKind; + readonly targetFlags: SymbolFlags; + readonly symbol: Symbol; +} + +/** @internal */ +export interface FixInfo { + readonly fix: ImportFix; + readonly symbolName: string; + readonly errorIdentifierText: string | undefined; + readonly isJsxNamespaceFix?: boolean; +} + +/** @internal */ +export function createImportAdder(sourceFile: SourceFile, program: Program, preferences: UserPreferences, host: LanguageServiceHost, useAutoImportProvider: boolean, cancellationToken?: CancellationToken): ImportAdder { + return createImportAdderWorker(sourceFile, program, useAutoImportProvider, preferences, host, cancellationToken); +} + +/** @internal */ +export function createImportSpecifierResolver(importingFile: SourceFile, program: Program, host: LanguageServiceHost, preferences: UserPreferences): ImportSpecifierResolver { + const packageJsonImportFilter = createPackageJsonImportFilter(importingFile, preferences, host); + const importMap = createExistingImportMap(program.getTypeChecker(), importingFile, program.getCompilerOptions()); + return { getModuleSpecifierForBestExportInfo }; + + function getModuleSpecifierForBestExportInfo( + exportInfo: readonly SymbolExportInfo[], + position: number, + isValidTypeOnlyUseSite: boolean, + fromCacheOnly?: boolean, + ): { exportInfo?: SymbolExportInfo, moduleSpecifier: string, computedWithoutCacheCount: number } | undefined { + const { fixes, computedWithoutCacheCount } = getImportFixes( + exportInfo, + position, + isValidTypeOnlyUseSite, + /*useRequire*/ false, + program, + importingFile, + host, + preferences, + importMap, + fromCacheOnly); + const result = getBestFix(fixes, importingFile, program, packageJsonImportFilter, host); + return result && { ...result, computedWithoutCacheCount }; + } +} + +/** @internal */ +export function getImportCompletionAction( + targetSymbol: Symbol, + moduleSymbol: Symbol, + exportMapKey: string | undefined, + sourceFile: SourceFile, + symbolName: string, + isJsxTagName: boolean, + host: LanguageServiceHost, + program: Program, + formatContext: FormatContext, + position: number, + preferences: UserPreferences, + cancellationToken: CancellationToken, +): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } { + const compilerOptions = program.getCompilerOptions(); + let exportInfos; + if (exportMapKey) { + // The new way: `exportMapKey` should be in the `data` of each auto-import completion entry and + // sent back when asking for details. + exportInfos = getExportInfoMap(sourceFile, host, program, preferences, cancellationToken).get(sourceFile.path, exportMapKey); + Debug.assertIsDefined(exportInfos, "Some exportInfo should match the specified exportMapKey"); + } + else { + // The old way, kept alive for super old editors that don't give us `data` back. + exportInfos = pathIsBareSpecifier(stripQuotes(moduleSymbol.name)) + ? [getSingleExportInfoForSymbol(targetSymbol, symbolName, moduleSymbol, program, host)] + : getAllExportInfoForSymbol(sourceFile, targetSymbol, symbolName, moduleSymbol, isJsxTagName, program, host, preferences, cancellationToken); + Debug.assertIsDefined(exportInfos, "Some exportInfo should match the specified symbol / moduleSymbol"); + } + const useRequire = shouldUseRequire(sourceFile, program); + const isValidTypeOnlyUseSite = isValidTypeOnlyAliasUseSite(getTokenAtPosition(sourceFile, position)); + const fix = Debug.checkDefined(getImportFixForSymbol(sourceFile, exportInfos, program, position, isValidTypeOnlyUseSite, useRequire, host, preferences)); + return { + moduleSpecifier: fix.moduleSpecifier, + codeAction: codeFixActionToCodeAction(codeActionForFix( + { host, formatContext, preferences }, + sourceFile, + symbolName, + fix, + /*includeSymbolNameInDescription*/ false, + compilerOptions, + preferences)) + }; +} + +/** @internal */ +export function getPromoteTypeOnlyCompletionAction(sourceFile: SourceFile, symbolToken: Identifier, program: Program, host: LanguageServiceHost, formatContext: FormatContext, preferences: UserPreferences) { + const compilerOptions = program.getCompilerOptions(); + const symbolName = single(getSymbolNamesToImport(sourceFile, program.getTypeChecker(), symbolToken, compilerOptions)); + const fix = getTypeOnlyPromotionFix(sourceFile, symbolToken, symbolName, program); + const includeSymbolNameInDescription = symbolName !== symbolToken.text; + return fix && codeFixActionToCodeAction(codeActionForFix({ host, formatContext, preferences }, sourceFile, symbolName, fix, includeSymbolNameInDescription, compilerOptions, preferences)); +} + +/** + * @param forceImportKeyword Indicates that the user has already typed `import`, so the result must start with `import`. + * (In other words, do not allow `const x = require("...")` for JS files.) + * + * @internal + */ +export function getImportKind(importingFile: SourceFile, exportKind: ExportKind, compilerOptions: CompilerOptions, forceImportKeyword?: boolean): ImportKind { + if (compilerOptions.verbatimModuleSyntax && (getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS || importingFile.impliedNodeFormat === ModuleKind.CommonJS)) { + // TODO: if the exporting file is ESM under nodenext, or `forceImport` is given in a JS file, this is impossible + return ImportKind.CommonJS; + } + switch (exportKind) { + case ExportKind.Named: return ImportKind.Named; + case ExportKind.Default: return ImportKind.Default; + case ExportKind.ExportEquals: return getExportEqualsImportKind(importingFile, compilerOptions, !!forceImportKeyword); + case ExportKind.UMD: return getUmdImportKind(importingFile, compilerOptions, !!forceImportKeyword); + default: return Debug.assertNever(exportKind); + } +} + +/** @internal */ +export function getFixInfos(context: CodeFixContextBase, errorCode: number, pos: number, useAutoImportProvider: boolean): readonly FixInfo[] | undefined { + const symbolToken = getTokenAtPosition(context.sourceFile, pos); + let info; + if (errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code) { + info = getFixesInfoForUMDImport(context, symbolToken); + } + else if (!isIdentifier(symbolToken)) { + return undefined; + } + else if (errorCode === Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type.code) { + const symbolName = single(getSymbolNamesToImport(context.sourceFile, context.program.getTypeChecker(), symbolToken, context.program.getCompilerOptions())); + const fix = getTypeOnlyPromotionFix(context.sourceFile, symbolToken, symbolName, context.program); + return fix && [{ fix, symbolName, errorIdentifierText: symbolToken.text }]; + } + else { + info = getFixesInfoForNonUMDImport(context, symbolToken, useAutoImportProvider); + } + + const packageJsonImportFilter = createPackageJsonImportFilter(context.sourceFile, context.preferences, context.host); + return info && sortFixInfo(info, context.sourceFile, context.program, packageJsonImportFilter, context.host); +} + +/** @internal */ +export function codeActionForFix(context: TextChangesContext, sourceFile: SourceFile, symbolName: string, fix: ImportFix, includeSymbolNameInDescription: boolean, compilerOptions: CompilerOptions, preferences: UserPreferences): CodeFixAction { + let diag!: DiagnosticOrDiagnosticAndArguments; + const changes = ChangeTracker.with(context, tracker => { + diag = codeActionForFixWorker(tracker, sourceFile, symbolName, fix, includeSymbolNameInDescription, compilerOptions, preferences); + }); + return createCodeFixAction(fixName, changes, diag, fixId, Diagnostics.Add_all_missing_imports); +} + +/** @internal */ +export function moduleSymbolToValidIdentifier(moduleSymbol: Symbol, target: ScriptTarget | undefined, forceCapitalize: boolean): string { + return moduleSpecifierToValidIdentifier(removeFileExtension(stripQuotes(moduleSymbol.name)), target, forceCapitalize); +} + +/** @internal */ +export function moduleSpecifierToValidIdentifier(moduleSpecifier: string, target: ScriptTarget | undefined, forceCapitalize?: boolean): string { + const baseName = getBaseFileName(removeSuffix(moduleSpecifier, "/index")); + let res = ""; + let lastCharWasValid = true; + const firstCharCode = baseName.charCodeAt(0); + if (isIdentifierStart(firstCharCode, target)) { + res += String.fromCharCode(firstCharCode); + if (forceCapitalize) { + res = res.toUpperCase(); + } + } + else { + lastCharWasValid = false; + } + for (let i = 1; i < baseName.length; i++) { + const ch = baseName.charCodeAt(i); + const isValid = isIdentifierPart(ch, target); + if (isValid) { + let char = String.fromCharCode(ch); + if (!lastCharWasValid) { + char = char.toUpperCase(); + } + res += char; + } + lastCharWasValid = isValid; + } + // Need `|| "_"` to ensure result isn't empty. + return !isStringANonContextualKeyword(res) ? res || "_" : `_${res}`; +} + +function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAutoImportProvider: boolean, preferences: UserPreferences, host: LanguageServiceHost, cancellationToken: CancellationToken | undefined): ImportAdder { + const compilerOptions = program.getCompilerOptions(); + // Namespace fixes don't conflict, so just build a list. + const addToNamespace: FixUseNamespaceImport[] = []; + const importType: FixAddJsdocTypeImport[] = []; + /** Keys are import clause node IDs. */ + const addToExisting = new Map(); + + type NewImportsKey = `${0 | 1}|${string}`; + /** Use `getNewImportEntry` for access */ + const newImports = new Map>(); + return { addImportFromDiagnostic, addImportFromExportedSymbol, writeFixes, hasFixes }; + + function addImportFromDiagnostic(diagnostic: DiagnosticWithLocation, context: CodeFixContextBase) { + const info = getFixInfos(context, diagnostic.code, diagnostic.start, useAutoImportProvider); + if (!info || !info.length) return; + addImport(first(info)); + } + + function addImportFromExportedSymbol(exportedSymbol: Symbol, isValidTypeOnlyUseSite?: boolean) { + const moduleSymbol = Debug.checkDefined(exportedSymbol.parent); + const symbolName = getNameForExportedSymbol(exportedSymbol, getEmitScriptTarget(compilerOptions)); + const checker = program.getTypeChecker(); + const symbol = checker.getMergedSymbol(skipAlias(exportedSymbol, checker)); + const exportInfo = getAllExportInfoForSymbol(sourceFile, symbol, symbolName, moduleSymbol, /*preferCapitalized*/ false, program, host, preferences, cancellationToken); + const useRequire = shouldUseRequire(sourceFile, program); + const fix = getImportFixForSymbol(sourceFile, Debug.checkDefined(exportInfo), program, /*position*/ undefined, !!isValidTypeOnlyUseSite, useRequire, host, preferences); + if (fix) { + addImport({ fix, symbolName, errorIdentifierText: undefined }); + } + } + + function addImport(info: FixInfo) { + const { fix, symbolName } = info; + switch (fix.kind) { + case ImportFixKind.UseNamespace: + addToNamespace.push(fix); + break; + case ImportFixKind.JsdocTypeImport: + importType.push(fix); + break; + case ImportFixKind.AddToExisting: { + const { importClauseOrBindingPattern, importKind, addAsTypeOnly } = fix; + const key = String(getNodeId(importClauseOrBindingPattern)); + let entry = addToExisting.get(key); + if (!entry) { + addToExisting.set(key, entry = { importClauseOrBindingPattern, defaultImport: undefined, namedImports: new Map() }); + } + if (importKind === ImportKind.Named) { + const prevValue = entry?.namedImports.get(symbolName); + entry.namedImports.set(symbolName, reduceAddAsTypeOnlyValues(prevValue, addAsTypeOnly)); + } + else { + Debug.assert(entry.defaultImport === undefined || entry.defaultImport.name === symbolName, "(Add to Existing) Default import should be missing or match symbolName"); + entry.defaultImport = { + name: symbolName, + addAsTypeOnly: reduceAddAsTypeOnlyValues(entry.defaultImport?.addAsTypeOnly, addAsTypeOnly), + }; + } + break; + } + case ImportFixKind.AddNew: { + const { moduleSpecifier, importKind, useRequire, addAsTypeOnly } = fix; + const entry = getNewImportEntry(moduleSpecifier, importKind, useRequire, addAsTypeOnly); + Debug.assert(entry.useRequire === useRequire, "(Add new) Tried to add an `import` and a `require` for the same module"); + + switch (importKind) { + case ImportKind.Default: + Debug.assert(entry.defaultImport === undefined || entry.defaultImport.name === symbolName, "(Add new) Default import should be missing or match symbolName"); + entry.defaultImport = { name: symbolName, addAsTypeOnly: reduceAddAsTypeOnlyValues(entry.defaultImport?.addAsTypeOnly, addAsTypeOnly) }; + break; + case ImportKind.Named: + const prevValue = (entry.namedImports ||= new Map()).get(symbolName); + entry.namedImports.set(symbolName, reduceAddAsTypeOnlyValues(prevValue, addAsTypeOnly)); + break; + case ImportKind.CommonJS: + case ImportKind.Namespace: + Debug.assert(entry.namespaceLikeImport === undefined || entry.namespaceLikeImport.name === symbolName, "Namespacelike import shoudl be missing or match symbolName"); + entry.namespaceLikeImport = { importKind, name: symbolName, addAsTypeOnly }; + break; + } + break; + } + case ImportFixKind.PromoteTypeOnly: + // Excluding from fix-all + break; + default: + Debug.assertNever(fix, `fix wasn't never - got kind ${(fix as ImportFix).kind}`); + } + + function reduceAddAsTypeOnlyValues(prevValue: AddAsTypeOnly | undefined, newValue: AddAsTypeOnly): AddAsTypeOnly { + // `NotAllowed` overrides `Required` because one addition of a new import might be required to be type-only + // because of `--importsNotUsedAsValues=error`, but if a second addition of the same import is `NotAllowed` + // to be type-only, the reason the first one was `Required` - the unused runtime dependency - is now moot. + // Alternatively, if one addition is `Required` because it has no value meaning under `--preserveValueImports` + // and `--isolatedModules`, it should be impossible for another addition to be `NotAllowed` since that would + // mean a type is being referenced in a value location. + return Math.max(prevValue ?? 0, newValue); + } + + function getNewImportEntry(moduleSpecifier: string, importKind: ImportKind, useRequire: boolean, addAsTypeOnly: AddAsTypeOnly): Mutable { + // A default import that requires type-only makes the whole import type-only. + // (We could add `default` as a named import, but that style seems undesirable.) + // Under `--preserveValueImports` and `--importsNotUsedAsValues=error`, if a + // module default-exports a type but named-exports some values (weird), you would + // have to use a type-only default import and non-type-only named imports. These + // require two separate import declarations, so we build this into the map key. + const typeOnlyKey = newImportsKey(moduleSpecifier, /*topLevelTypeOnly*/ true); + const nonTypeOnlyKey = newImportsKey(moduleSpecifier, /*topLevelTypeOnly*/ false); + const typeOnlyEntry = newImports.get(typeOnlyKey); + const nonTypeOnlyEntry = newImports.get(nonTypeOnlyKey); + const newEntry: ImportsCollection & { useRequire: boolean } = { + defaultImport: undefined, + namedImports: undefined, + namespaceLikeImport: undefined, + useRequire + }; + if (importKind === ImportKind.Default && addAsTypeOnly === AddAsTypeOnly.Required) { + if (typeOnlyEntry) return typeOnlyEntry; + newImports.set(typeOnlyKey, newEntry); + return newEntry; + } + if (addAsTypeOnly === AddAsTypeOnly.Allowed && (typeOnlyEntry || nonTypeOnlyEntry)) { + return (typeOnlyEntry || nonTypeOnlyEntry)!; + } + if (nonTypeOnlyEntry) { + return nonTypeOnlyEntry; + } + newImports.set(nonTypeOnlyKey, newEntry); + return newEntry; + } + + function newImportsKey(moduleSpecifier: string, topLevelTypeOnly: boolean): NewImportsKey { + return `${topLevelTypeOnly ? 1 : 0}|${moduleSpecifier}`; + } + } + + function writeFixes(changeTracker: ChangeTracker) { + const quotePreference = getQuotePreference(sourceFile, preferences); + for (const fix of addToNamespace) { + addNamespaceQualifier(changeTracker, sourceFile, fix); + } + for (const fix of importType) { + addImportType(changeTracker, sourceFile, fix, quotePreference); + } + addToExisting.forEach(({ importClauseOrBindingPattern, defaultImport, namedImports }) => { + doAddExistingFix( + changeTracker, + sourceFile, + importClauseOrBindingPattern, + defaultImport, + arrayFrom(namedImports.entries(), ([name, addAsTypeOnly]) => ({ addAsTypeOnly, name })), + preferences); + }); + + let newDeclarations: AnyImportOrRequireStatement | readonly AnyImportOrRequireStatement[] | undefined; + newImports.forEach(({ useRequire, defaultImport, namedImports, namespaceLikeImport }, key) => { + const moduleSpecifier = key.slice(2); // From `${0 | 1}|${moduleSpecifier}` format + const getDeclarations = useRequire ? getNewRequires : getNewImports; + const declarations = getDeclarations( + moduleSpecifier, + quotePreference, + defaultImport, + namedImports && arrayFrom(namedImports.entries(), ([name, addAsTypeOnly]) => ({ addAsTypeOnly, name })), + namespaceLikeImport, + compilerOptions); + newDeclarations = combine(newDeclarations, declarations); + }); + if (newDeclarations) { + insertImports(changeTracker, sourceFile, newDeclarations, /*blankLineBetween*/ true, preferences); + } + } + + function hasFixes() { + return addToNamespace.length > 0 || importType.length > 0 || addToExisting.size > 0 || newImports.size > 0; + } +} + +function getImportFixForSymbol(sourceFile: SourceFile, exportInfos: readonly SymbolExportInfo[], program: Program, position: number | undefined, isValidTypeOnlyUseSite: boolean, useRequire: boolean, host: LanguageServiceHost, preferences: UserPreferences) { + const packageJsonImportFilter = createPackageJsonImportFilter(sourceFile, preferences, host); + return getBestFix(getImportFixes(exportInfos, position, isValidTypeOnlyUseSite, useRequire, program, sourceFile, host, preferences).fixes, sourceFile, program, packageJsonImportFilter, host); +} + +function codeFixActionToCodeAction({ description, changes, commands }: CodeFixAction): CodeAction { + return { description, changes, commands }; +} + +function getAllExportInfoForSymbol(importingFile: SourceFile, symbol: Symbol, symbolName: string, moduleSymbol: Symbol, preferCapitalized: boolean, program: Program, host: LanguageServiceHost, preferences: UserPreferences, cancellationToken: CancellationToken | undefined): readonly SymbolExportInfo[] | undefined { + const getChecker = createGetChecker(program, host); + return getExportInfoMap(importingFile, host, program, preferences, cancellationToken) + .search(importingFile.path, preferCapitalized, name => name === symbolName, info => { + if (skipAlias(info[0].symbol, getChecker(info[0].isFromPackageJson)) === symbol && info.some(i => i.moduleSymbol === moduleSymbol || i.symbol.parent === moduleSymbol)) { + return info; + } + }); +} + +function getSingleExportInfoForSymbol(symbol: Symbol, symbolName: string, moduleSymbol: Symbol, program: Program, host: LanguageServiceHost): SymbolExportInfo { + const compilerOptions = program.getCompilerOptions(); + const mainProgramInfo = getInfoWithChecker(program.getTypeChecker(), /*isFromPackageJson*/ false); + if (mainProgramInfo) { + return mainProgramInfo; + } + const autoImportProvider = host.getPackageJsonAutoImportProvider?.()?.getTypeChecker(); + return Debug.checkDefined(autoImportProvider && getInfoWithChecker(autoImportProvider, /*isFromPackageJson*/ true), `Could not find symbol in specified module for code actions`); + + function getInfoWithChecker(checker: TypeChecker, isFromPackageJson: boolean): SymbolExportInfo | undefined { + const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions); + if (defaultInfo && skipAlias(defaultInfo.symbol, checker) === symbol) { + return { symbol: defaultInfo.symbol, moduleSymbol, moduleFileName: undefined, exportKind: defaultInfo.exportKind, targetFlags: skipAlias(symbol, checker).flags, isFromPackageJson }; + } + const named = checker.tryGetMemberInModuleExportsAndProperties(symbolName, moduleSymbol); + if (named && skipAlias(named, checker) === symbol) { + return { symbol: named, moduleSymbol, moduleFileName: undefined, exportKind: ExportKind.Named, targetFlags: skipAlias(symbol, checker).flags, isFromPackageJson }; + } + } +} + +function getImportFixes( + exportInfos: readonly SymbolExportInfo[], + usagePosition: number | undefined, + /** undefined only for missing JSX namespace */ + isValidTypeOnlyUseSite: boolean, + useRequire: boolean, + program: Program, + sourceFile: SourceFile, + host: LanguageServiceHost, + preferences: UserPreferences, + importMap = createExistingImportMap(program.getTypeChecker(), sourceFile, program.getCompilerOptions()), + fromCacheOnly?: boolean, +): { computedWithoutCacheCount: number, fixes: readonly ImportFixWithModuleSpecifier[] } { + const checker = program.getTypeChecker(); + const existingImports = flatMap(exportInfos, importMap.getImportsForExportInfo); + const useNamespace = usagePosition !== undefined && tryUseExistingNamespaceImport(existingImports, usagePosition); + const addToExisting = tryAddToExistingImport(existingImports, isValidTypeOnlyUseSite, checker, program.getCompilerOptions()); + if (addToExisting) { + // Don't bother providing an action to add a new import if we can add to an existing one. + return { + computedWithoutCacheCount: 0, + fixes: [...(useNamespace ? [useNamespace] : emptyArray), addToExisting], + }; + } + + const { fixes, computedWithoutCacheCount = 0 } = getFixesForAddImport( + exportInfos, + existingImports, + program, + sourceFile, + usagePosition, + isValidTypeOnlyUseSite, + useRequire, + host, + preferences, + fromCacheOnly); + return { + computedWithoutCacheCount, + fixes: [...(useNamespace ? [useNamespace] : emptyArray), ...fixes], + }; +} + +function tryUseExistingNamespaceImport(existingImports: readonly FixAddToExistingImportInfo[], position: number): FixUseNamespaceImport | undefined { + // 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. + return firstDefined(existingImports, ({ declaration, importKind }): FixUseNamespaceImport | undefined => { + if (importKind !== ImportKind.Named) return undefined; + const namespacePrefix = getNamespaceLikeImportText(declaration); + const moduleSpecifier = namespacePrefix && tryGetModuleSpecifierFromDeclaration(declaration)?.text; + if (moduleSpecifier) { + return { kind: ImportFixKind.UseNamespace, namespacePrefix, usagePosition: position, moduleSpecifier }; + } + }); +} + +function getNamespaceLikeImportText(declaration: AnyImportOrRequire) { + switch (declaration.kind) { + case SyntaxKind.VariableDeclaration: + return tryCast(declaration.name, isIdentifier)?.text; + case SyntaxKind.ImportEqualsDeclaration: + return declaration.name.text; + case SyntaxKind.ImportDeclaration: + return tryCast(declaration.importClause?.namedBindings, isNamespaceImport)?.name.text; + default: + return Debug.assertNever(declaration); + } +} + +function getAddAsTypeOnly( + isValidTypeOnlyUseSite: boolean, + isForNewImportDeclaration: boolean, + symbol: Symbol, + targetFlags: SymbolFlags, + checker: TypeChecker, + compilerOptions: CompilerOptions +) { + if (!isValidTypeOnlyUseSite) { + // Can't use a type-only import if the usage is an emitting position + return AddAsTypeOnly.NotAllowed; + } + if (isForNewImportDeclaration && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error) { + // Not writing a (top-level) type-only import here would create an error because the runtime dependency is unnecessary + return AddAsTypeOnly.Required; + } + if (importNameElisionDisabled(compilerOptions) && + (!(targetFlags & SymbolFlags.Value) || !!checker.getTypeOnlyAliasDeclaration(symbol)) + ) { + // A type-only import is required for this symbol if under these settings if the symbol will + // be erased, which will happen if the target symbol is purely a type or if it was exported/imported + // as type-only already somewhere between this import and the target. + return AddAsTypeOnly.Required; + } + return AddAsTypeOnly.Allowed; +} + +function tryAddToExistingImport(existingImports: readonly FixAddToExistingImportInfo[], isValidTypeOnlyUseSite: boolean, checker: TypeChecker, compilerOptions: CompilerOptions): FixAddToExistingImport | undefined { + let best: FixAddToExistingImport | undefined; + for (const existingImport of existingImports) { + const fix = getAddToExistingImportFix(existingImport); + if (!fix) continue; + const isTypeOnly = isTypeOnlyImportDeclaration(fix.importClauseOrBindingPattern); + if ( + fix.addAsTypeOnly !== AddAsTypeOnly.NotAllowed && isTypeOnly || + fix.addAsTypeOnly === AddAsTypeOnly.NotAllowed && !isTypeOnly + ) { + // Give preference to putting types in existing type-only imports and avoiding conversions + // of import statements to/from type-only. + return fix; + } + best ??= fix; + } + return best; + + function getAddToExistingImportFix({ declaration, importKind, symbol, targetFlags }: FixAddToExistingImportInfo): FixAddToExistingImport | undefined { + if (importKind === ImportKind.CommonJS || importKind === ImportKind.Namespace || declaration.kind === SyntaxKind.ImportEqualsDeclaration) { + // These kinds of imports are not combinable with anything + return undefined; + } + + if (declaration.kind === SyntaxKind.VariableDeclaration) { + return (importKind === ImportKind.Named || importKind === ImportKind.Default) && declaration.name.kind === SyntaxKind.ObjectBindingPattern + ? { kind: ImportFixKind.AddToExisting, importClauseOrBindingPattern: declaration.name, importKind, moduleSpecifier: declaration.initializer.arguments[0].text, addAsTypeOnly: AddAsTypeOnly.NotAllowed } + : undefined; + } + + const { importClause } = declaration; + if (!importClause || !isStringLiteralLike(declaration.moduleSpecifier)) { + return undefined; + } + const { name, namedBindings } = importClause; + // A type-only import may not have both a default and named imports, so the only way a name can + // be added to an existing type-only import is adding a named import to existing named bindings. + if (importClause.isTypeOnly && !(importKind === ImportKind.Named && namedBindings)) { + return undefined; + } + + // N.B. we don't have to figure out whether to use the main program checker + // or the AutoImportProvider checker because we're adding to an existing import; the existence of + // the import guarantees the symbol came from the main program. + const addAsTypeOnly = getAddAsTypeOnly(isValidTypeOnlyUseSite, /*isForNewImportDeclaration*/ false, symbol, targetFlags, checker, compilerOptions); + + if (importKind === ImportKind.Default && ( + name || // Cannot add a default import to a declaration that already has one + addAsTypeOnly === AddAsTypeOnly.Required && namedBindings // Cannot add a default import as type-only if the import already has named bindings + )) { + return undefined; + } + if (importKind === ImportKind.Named && + namedBindings?.kind === SyntaxKind.NamespaceImport // Cannot add a named import to a declaration that has a namespace import + ) { + return undefined; + } + + return { + kind: ImportFixKind.AddToExisting, + importClauseOrBindingPattern: importClause, + importKind, + moduleSpecifier: declaration.moduleSpecifier.text, + addAsTypeOnly, + }; + } +} + +function createExistingImportMap(checker: TypeChecker, importingFile: SourceFile, compilerOptions: CompilerOptions) { + let importMap: MultiMap | undefined; + for (const moduleSpecifier of importingFile.imports) { + const i = importFromModuleSpecifier(moduleSpecifier); + if (isVariableDeclarationInitializedToRequire(i.parent)) { + const moduleSymbol = checker.resolveExternalModuleName(moduleSpecifier); + if (moduleSymbol) { + (importMap ||= createMultiMap()).add(getSymbolId(moduleSymbol), i.parent); + } + } + else if (i.kind === SyntaxKind.ImportDeclaration || i.kind === SyntaxKind.ImportEqualsDeclaration) { + const moduleSymbol = checker.getSymbolAtLocation(moduleSpecifier); + if (moduleSymbol) { + (importMap ||= createMultiMap()).add(getSymbolId(moduleSymbol), i); + } + } + } + + return { + getImportsForExportInfo: ({ moduleSymbol, exportKind, targetFlags, symbol }: SymbolExportInfo): readonly FixAddToExistingImportInfo[] => { + // Can't use an es6 import for a type in JS. + if (!(targetFlags & SymbolFlags.Value) && isSourceFileJS(importingFile)) return emptyArray; + const matchingDeclarations = importMap?.get(getSymbolId(moduleSymbol)); + if (!matchingDeclarations) return emptyArray; + const importKind = getImportKind(importingFile, exportKind, compilerOptions); + return matchingDeclarations.map(declaration => ({ declaration, importKind, symbol, targetFlags })); + } + }; +} + +function shouldUseRequire(sourceFile: SourceFile, program: Program): boolean { + // 1. TypeScript files don't use require variable declarations + if (!isSourceFileJS(sourceFile)) { + return false; + } + + // 2. If the current source file is unambiguously CJS or ESM, go with that + if (sourceFile.commonJsModuleIndicator && !sourceFile.externalModuleIndicator) return true; + if (sourceFile.externalModuleIndicator && !sourceFile.commonJsModuleIndicator) return false; + + // 3. If there's a tsconfig/jsconfig, use its module setting + const compilerOptions = program.getCompilerOptions(); + if (compilerOptions.configFile) { + return getEmitModuleKind(compilerOptions) < ModuleKind.ES2015; + } + + // 4. Match the first other JS file in the program that's unambiguously CJS or ESM + for (const otherFile of program.getSourceFiles()) { + if (otherFile === sourceFile || !isSourceFileJS(otherFile) || program.isSourceFileFromExternalLibrary(otherFile)) continue; + if (otherFile.commonJsModuleIndicator && !otherFile.externalModuleIndicator) return true; + if (otherFile.externalModuleIndicator && !otherFile.commonJsModuleIndicator) return false; + } + + // 5. Literally nothing to go on + return true; +} + +function createGetChecker(program: Program, host: LanguageServiceHost) { + return memoizeOne((isFromPackageJson: boolean) => isFromPackageJson ? host.getPackageJsonAutoImportProvider!()!.getTypeChecker() : program.getTypeChecker()); +} + +function getNewImportFixes( + program: Program, + sourceFile: SourceFile, + usagePosition: number | undefined, + isValidTypeOnlyUseSite: boolean, + useRequire: boolean, + exportInfo: readonly SymbolExportInfo[], + host: LanguageServiceHost, + preferences: UserPreferences, + fromCacheOnly?: boolean, +): { computedWithoutCacheCount: number, fixes: readonly (FixAddNewImport | FixAddJsdocTypeImport)[] } { + const isJs = isSourceFileJS(sourceFile); + const compilerOptions = program.getCompilerOptions(); + const moduleSpecifierResolutionHost = createModuleSpecifierResolutionHost(program, host); + const getChecker = createGetChecker(program, host); + const moduleResolution = getEmitModuleResolutionKind(compilerOptions); + const rejectNodeModulesRelativePaths = moduleResolutionUsesNodeModules(moduleResolution); + const getModuleSpecifiers = fromCacheOnly + ? (moduleSymbol: Symbol) => ({ moduleSpecifiers: tryGetModuleSpecifiersFromCache(moduleSymbol, sourceFile, moduleSpecifierResolutionHost, preferences), computedWithoutCache: false }) + : (moduleSymbol: Symbol, checker: TypeChecker) => getModuleSpecifiersWithCacheInfo(moduleSymbol, checker, compilerOptions, sourceFile, moduleSpecifierResolutionHost, preferences); + + let computedWithoutCacheCount = 0; + const fixes = flatMap(exportInfo, (exportInfo, i) => { + const checker = getChecker(exportInfo.isFromPackageJson); + const { computedWithoutCache, moduleSpecifiers } = getModuleSpecifiers(exportInfo.moduleSymbol, checker); + const importedSymbolHasValueMeaning = !!(exportInfo.targetFlags & SymbolFlags.Value); + const addAsTypeOnly = getAddAsTypeOnly(isValidTypeOnlyUseSite, /*isForNewImportDeclaration*/ true, exportInfo.symbol, exportInfo.targetFlags, checker, compilerOptions); + computedWithoutCacheCount += computedWithoutCache ? 1 : 0; + return mapDefined(moduleSpecifiers, (moduleSpecifier): FixAddNewImport | FixAddJsdocTypeImport | undefined => { + if (rejectNodeModulesRelativePaths && pathContainsNodeModules(moduleSpecifier)) { + return undefined; + } + if (!importedSymbolHasValueMeaning && isJs && usagePosition !== undefined) { + // `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types. + return { kind: ImportFixKind.JsdocTypeImport, moduleSpecifier, usagePosition, exportInfo, isReExport: i > 0 }; + } + const importKind = getImportKind(sourceFile, exportInfo.exportKind, compilerOptions); + let qualification: Qualification | undefined; + if (usagePosition !== undefined && importKind === ImportKind.CommonJS && exportInfo.exportKind === ExportKind.Named) { + // Compiler options are restricting our import options to a require, but we need to access + // a named export or property of the exporting module. We need to import the entire module + // and insert a property access, e.g. `writeFile` becomes + // + // import fs = require("fs"); // or const in JS + // fs.writeFile + const exportEquals = checker.resolveExternalModuleSymbol(exportInfo.moduleSymbol); + let namespacePrefix; + if (exportEquals !== exportInfo.moduleSymbol) { + namespacePrefix = getDefaultExportInfoWorker(exportEquals, checker, compilerOptions)?.name; + } + namespacePrefix ||= moduleSymbolToValidIdentifier( + exportInfo.moduleSymbol, + getEmitScriptTarget(compilerOptions), + /*forceCapitalize*/ false); + qualification = { namespacePrefix, usagePosition }; + } + return { + kind: ImportFixKind.AddNew, + moduleSpecifier, + importKind, + useRequire, + addAsTypeOnly, + exportInfo, + isReExport: i > 0, + qualification, + }; + }); + }); + + return { computedWithoutCacheCount, fixes }; +} + +function getFixesForAddImport( + exportInfos: readonly SymbolExportInfo[], + existingImports: readonly FixAddToExistingImportInfo[], + program: Program, + sourceFile: SourceFile, + usagePosition: number | undefined, + isValidTypeOnlyUseSite: boolean, + useRequire: boolean, + host: LanguageServiceHost, + preferences: UserPreferences, + fromCacheOnly?: boolean, +): { computedWithoutCacheCount?: number, fixes: readonly (FixAddNewImport | FixAddJsdocTypeImport)[] } { + const existingDeclaration = firstDefined(existingImports, info => newImportInfoFromExistingSpecifier(info, isValidTypeOnlyUseSite, useRequire, program.getTypeChecker(), program.getCompilerOptions())); + return existingDeclaration ? { fixes: [existingDeclaration] } : getNewImportFixes(program, sourceFile, usagePosition, isValidTypeOnlyUseSite, useRequire, exportInfos, host, preferences, fromCacheOnly); +} + +function newImportInfoFromExistingSpecifier( + { declaration, importKind, symbol, targetFlags }: FixAddToExistingImportInfo, + isValidTypeOnlyUseSite: boolean, + useRequire: boolean, + checker: TypeChecker, + compilerOptions: CompilerOptions +): FixAddNewImport | undefined { + const moduleSpecifier = tryGetModuleSpecifierFromDeclaration(declaration)?.text; + if (moduleSpecifier) { + const addAsTypeOnly = useRequire + ? AddAsTypeOnly.NotAllowed + : getAddAsTypeOnly(isValidTypeOnlyUseSite, /*isForNewImportDeclaration*/ true, symbol, targetFlags, checker, compilerOptions); + return { kind: ImportFixKind.AddNew, moduleSpecifier, importKind, addAsTypeOnly, useRequire }; + } +} + +function sortFixInfo(fixes: readonly (FixInfo & { fix: ImportFixWithModuleSpecifier })[], sourceFile: SourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter, host: LanguageServiceHost): readonly (FixInfo & { fix: ImportFixWithModuleSpecifier })[] { + const _toPath = (fileName: string) => toPath(fileName, host.getCurrentDirectory(), hostGetCanonicalFileName(host)); + return sort(fixes, (a, b) => + compareBooleans(!!a.isJsxNamespaceFix, !!b.isJsxNamespaceFix) || + compareValues(a.fix.kind, b.fix.kind) || + compareModuleSpecifiers(a.fix, b.fix, sourceFile, program, packageJsonImportFilter.allowsImportingSpecifier, _toPath)); +} + +function getBestFix(fixes: readonly ImportFixWithModuleSpecifier[], sourceFile: SourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter, host: LanguageServiceHost): ImportFixWithModuleSpecifier | undefined { + if (!some(fixes)) return; + // These will always be placed first if available, and are better than other kinds + if (fixes[0].kind === ImportFixKind.UseNamespace || fixes[0].kind === ImportFixKind.AddToExisting) { + return fixes[0]; + } + + return fixes.reduce((best, fix) => + // Takes true branch of conditional if `fix` is better than `best` + compareModuleSpecifiers( + fix, + best, + sourceFile, + program, + packageJsonImportFilter.allowsImportingSpecifier, + fileName => toPath(fileName, host.getCurrentDirectory(), hostGetCanonicalFileName(host)), + ) === Comparison.LessThan ? fix : best + ); +} + +/** @returns `Comparison.LessThan` if `a` is better than `b`. */ +function compareModuleSpecifiers( + a: ImportFixWithModuleSpecifier, + b: ImportFixWithModuleSpecifier, + importingFile: SourceFile, + program: Program, + allowsImportingSpecifier: (specifier: string) => boolean, + toPath: (fileName: string) => Path, +): Comparison { + if (a.kind !== ImportFixKind.UseNamespace && b.kind !== ImportFixKind.UseNamespace) { + return compareBooleans(allowsImportingSpecifier(b.moduleSpecifier), allowsImportingSpecifier(a.moduleSpecifier)) + || compareNodeCoreModuleSpecifiers(a.moduleSpecifier, b.moduleSpecifier, importingFile, program) + || compareBooleans( + isFixPossiblyReExportingImportingFile(a, importingFile, program.getCompilerOptions(), toPath), + isFixPossiblyReExportingImportingFile(b, importingFile, program.getCompilerOptions(), toPath)) + || compareNumberOfDirectorySeparators(a.moduleSpecifier, b.moduleSpecifier); + } + return Comparison.EqualTo; +} + +// This is a simple heuristic to try to avoid creating an import cycle with a barrel re-export. +// E.g., do not `import { Foo } from ".."` when you could `import { Foo } from "../Foo"`. +// This can produce false positives or negatives if re-exports cross into sibling directories +// (e.g. `export * from "../whatever"`) or are not named "index" (we don't even try to consider +// this if we're in a resolution mode where you can't drop trailing "/index" from paths). +function isFixPossiblyReExportingImportingFile(fix: ImportFixWithModuleSpecifier, importingFile: SourceFile, compilerOptions: CompilerOptions, toPath: (fileName: string) => Path): boolean { + if (fix.isReExport && + fix.exportInfo?.moduleFileName && + getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node10 && + isIndexFileName(fix.exportInfo.moduleFileName) + ) { + const reExportDir = toPath(getDirectoryPath(fix.exportInfo.moduleFileName)); + return startsWith((importingFile.path), reExportDir); + } + return false; +} + +function isIndexFileName(fileName: string) { + return getBaseFileName(fileName, [".js", ".jsx", ".d.ts", ".ts", ".tsx"], /*ignoreCase*/ true) === "index"; +} + +function compareNodeCoreModuleSpecifiers(a: string, b: string, importingFile: SourceFile, program: Program): Comparison { + if (startsWith(a, "node:") && !startsWith(b, "node:")) return shouldUseUriStyleNodeCoreModules(importingFile, program) ? Comparison.LessThan : Comparison.GreaterThan; + if (startsWith(b, "node:") && !startsWith(a, "node:")) return shouldUseUriStyleNodeCoreModules(importingFile, program) ? Comparison.GreaterThan : Comparison.LessThan; + return Comparison.EqualTo; +} + +function getFixesInfoForUMDImport({ sourceFile, program, host, preferences }: CodeFixContextBase, token: Node): (FixInfo & { fix: ImportFixWithModuleSpecifier })[] | undefined { + const checker = program.getTypeChecker(); + const umdSymbol = getUmdSymbol(token, checker); + if (!umdSymbol) return undefined; + const symbol = checker.getAliasedSymbol(umdSymbol); + const symbolName = umdSymbol.name; + const exportInfo: readonly SymbolExportInfo[] = [{ symbol: umdSymbol, moduleSymbol: symbol, moduleFileName: undefined, exportKind: ExportKind.UMD, targetFlags: symbol.flags, isFromPackageJson: false }]; + const useRequire = shouldUseRequire(sourceFile, program); + // `usagePosition` is undefined because `token` may not actually be a usage of the symbol we're importing. + // For example, we might need to import `React` in order to use an arbitrary JSX tag. We could send a position + // for other UMD imports, but `usagePosition` is currently only used to insert a namespace qualification + // before a named import, like converting `writeFile` to `fs.writeFile` (whether `fs` is already imported or + // not), and this function will only be called for UMD symbols, which are necessarily an `export =`, not a + // named export. + const fixes = getImportFixes(exportInfo, /*usagePosition*/ undefined, /*isValidTypeOnlyUseSite*/ false, useRequire, program, sourceFile, host, preferences).fixes; + return fixes.map(fix => ({ fix, symbolName, errorIdentifierText: tryCast(token, isIdentifier)?.text })); +} + +function getUmdSymbol(token: Node, checker: TypeChecker): Symbol | undefined { + // try the identifier to see if it is the umd symbol + const umdSymbol = isIdentifier(token) ? checker.getSymbolAtLocation(token) : undefined; + if (isUMDExportSymbol(umdSymbol)) return umdSymbol; + + // The error wasn't for the symbolAtLocation, it was for the JSX tag itself, which needs access to e.g. `React`. + const { parent } = token; + if ((isJsxOpeningLikeElement(parent) && parent.tagName === token) || isJsxOpeningFragment(parent)) { + const parentSymbol = checker.resolveName(checker.getJsxNamespace(parent), isJsxOpeningLikeElement(parent) ? token : parent, SymbolFlags.Value, /*excludeGlobals*/ false); + if (isUMDExportSymbol(parentSymbol)) { + return parentSymbol; + } + } + return undefined; +} + +function getUmdImportKind(importingFile: SourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind { + // Import a synthetic `default` if enabled. + if (getAllowSyntheticDefaultImports(compilerOptions)) { + return ImportKind.Default; + } + + // When a synthetic `default` is unavailable, use `import..require` if the module kind supports it. + const moduleKind = getEmitModuleKind(compilerOptions); + switch (moduleKind) { + case ModuleKind.AMD: + case ModuleKind.CommonJS: + case ModuleKind.UMD: + if (isInJSFile(importingFile)) { + return isExternalModule(importingFile) || forceImportKeyword ? ImportKind.Namespace : ImportKind.CommonJS; + } + return ImportKind.CommonJS; + case ModuleKind.System: + case ModuleKind.ES2015: + case ModuleKind.ES2020: + case ModuleKind.ES2022: + case ModuleKind.ESNext: + case ModuleKind.None: + // Fall back to the `import * as ns` style import. + return ImportKind.Namespace; + case ModuleKind.Node16: + case ModuleKind.NodeNext: + return importingFile.impliedNodeFormat === ModuleKind.ESNext ? ImportKind.Namespace : ImportKind.CommonJS; + default: + return Debug.assertNever(moduleKind, `Unexpected moduleKind ${moduleKind}`); + } +} + +function getFixesInfoForNonUMDImport({ sourceFile, program, cancellationToken, host, preferences }: CodeFixContextBase, symbolToken: Identifier, useAutoImportProvider: boolean): readonly (FixInfo & { fix: ImportFixWithModuleSpecifier })[] | undefined { + const checker = program.getTypeChecker(); + const compilerOptions = program.getCompilerOptions(); + return flatMap(getSymbolNamesToImport(sourceFile, checker, symbolToken, compilerOptions), symbolName => { + // "default" is a keyword and not a legal identifier for the import, but appears as an identifier. + if (symbolName === InternalSymbolName.Default) { + return undefined; + } + const isValidTypeOnlyUseSite = isValidTypeOnlyAliasUseSite(symbolToken); + const useRequire = shouldUseRequire(sourceFile, program); + const exportInfo = getExportInfos(symbolName, isJSXTagName(symbolToken), getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, program, useAutoImportProvider, host, preferences); + return arrayFrom( + flatMapIterator(exportInfo.values(), exportInfos => + getImportFixes(exportInfos, symbolToken.getStart(sourceFile), isValidTypeOnlyUseSite, useRequire, program, sourceFile, host, preferences).fixes), + fix => ({ fix, symbolName, errorIdentifierText: symbolToken.text, isJsxNamespaceFix: symbolName !== symbolToken.text }) + ); + }); +} + +function getTypeOnlyPromotionFix(sourceFile: SourceFile, symbolToken: Identifier, symbolName: string, program: Program): FixPromoteTypeOnlyImport | undefined { + const checker = program.getTypeChecker(); + const symbol = checker.resolveName(symbolName, symbolToken, SymbolFlags.Value, /*excludeGlobals*/ true); + if (!symbol) return undefined; + + const typeOnlyAliasDeclaration = checker.getTypeOnlyAliasDeclaration(symbol); + if (!typeOnlyAliasDeclaration || getSourceFileOfNode(typeOnlyAliasDeclaration) !== sourceFile) return undefined; + + return { kind: ImportFixKind.PromoteTypeOnly, typeOnlyAliasDeclaration }; +} + +function getSymbolNamesToImport(sourceFile: SourceFile, checker: TypeChecker, symbolToken: Identifier, compilerOptions: CompilerOptions): string[] { + const parent = symbolToken.parent; + if ((isJsxOpeningLikeElement(parent) || isJsxClosingElement(parent)) && parent.tagName === symbolToken && jsxModeNeedsExplicitImport(compilerOptions.jsx)) { + const jsxNamespace = checker.getJsxNamespace(sourceFile); + if (needsJsxNamespaceFix(jsxNamespace, symbolToken, checker)) { + const needsComponentNameFix = !isIntrinsicJsxName(symbolToken.text) && !checker.resolveName(symbolToken.text, symbolToken, SymbolFlags.Value, /*excludeGlobals*/ false); + return needsComponentNameFix ? [symbolToken.text, jsxNamespace] : [jsxNamespace]; + } + } + return [symbolToken.text]; +} + +function needsJsxNamespaceFix(jsxNamespace: string, symbolToken: Identifier, checker: TypeChecker) { + if (isIntrinsicJsxName(symbolToken.text)) return true; // If we were triggered by a matching error code on an intrinsic, the error must have been about missing the JSX factory + const namespaceSymbol = checker.resolveName(jsxNamespace, symbolToken, SymbolFlags.Value, /*excludeGlobals*/ true); + return !namespaceSymbol || some(namespaceSymbol.declarations, isTypeOnlyImportOrExportDeclaration) && !(namespaceSymbol.flags & SymbolFlags.Value); +} + +// Returns a map from an exported symbol's ID to a list of every way it's (re-)exported. +function getExportInfos( + symbolName: string, + isJsxTagName: boolean, + currentTokenMeaning: SemanticMeaning, + cancellationToken: CancellationToken, + fromFile: SourceFile, + program: Program, + useAutoImportProvider: boolean, + host: LanguageServiceHost, + preferences: UserPreferences, +): ReadonlyMap { + // For each original symbol, keep all re-exports of that symbol together so we can call `getCodeActionsForImport` on the whole group at once. + // Maps symbol id to info for modules providing that symbol (original export + re-exports). + const originalSymbolToExportInfos = createMultiMap(); + const packageJsonFilter = createPackageJsonImportFilter(fromFile, preferences, host); + const moduleSpecifierCache = host.getModuleSpecifierCache?.(); + const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => { + return createModuleSpecifierResolutionHost(isFromPackageJson ? host.getPackageJsonAutoImportProvider!()! : program, host); + }); + function addSymbol(moduleSymbol: Symbol, toFile: SourceFile | undefined, exportedSymbol: Symbol, exportKind: ExportKind, program: Program, isFromPackageJson: boolean): void { + const moduleSpecifierResolutionHost = getModuleSpecifierResolutionHost(isFromPackageJson); + if (toFile && isImportableFile(program, fromFile, toFile, preferences, packageJsonFilter, moduleSpecifierResolutionHost, moduleSpecifierCache) || + !toFile && packageJsonFilter.allowsImportingAmbientModule(moduleSymbol, moduleSpecifierResolutionHost) + ) { + const checker = program.getTypeChecker(); + originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { symbol: exportedSymbol, moduleSymbol, moduleFileName: toFile?.fileName, exportKind, targetFlags: skipAlias(exportedSymbol, checker).flags, isFromPackageJson }); + } + } + forEachExternalModuleToImportFrom(program, host, preferences, useAutoImportProvider, (moduleSymbol, sourceFile, program, isFromPackageJson) => { + const checker = program.getTypeChecker(); + cancellationToken.throwIfCancellationRequested(); + + const compilerOptions = program.getCompilerOptions(); + const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions); + if (defaultInfo && (defaultInfo.name === symbolName || moduleSymbolToValidIdentifier(moduleSymbol, getEmitScriptTarget(compilerOptions), isJsxTagName) === symbolName) && symbolHasMeaning(defaultInfo.resolvedSymbol, currentTokenMeaning)) { + addSymbol(moduleSymbol, sourceFile, defaultInfo.symbol, defaultInfo.exportKind, program, isFromPackageJson); + } + + // check exports with the same name + const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExportsAndProperties(symbolName, moduleSymbol); + if (exportSymbolWithIdenticalName && symbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) { + addSymbol(moduleSymbol, sourceFile, exportSymbolWithIdenticalName, ExportKind.Named, program, isFromPackageJson); + } + }); + return originalSymbolToExportInfos; +} + +function getExportEqualsImportKind(importingFile: SourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind { + const allowSyntheticDefaults = getAllowSyntheticDefaultImports(compilerOptions); + const isJS = isInJSFile(importingFile); + // 1. 'import =' will not work in es2015+ TS files, so the decision is between a default + // and a namespace import, based on allowSyntheticDefaultImports/esModuleInterop. + if (!isJS && getEmitModuleKind(compilerOptions) >= ModuleKind.ES2015) { + return allowSyntheticDefaults ? ImportKind.Default : ImportKind.Namespace; + } + // 2. 'import =' will not work in JavaScript, so the decision is between a default import, + // a namespace import, and const/require. + if (isJS) { + return isExternalModule(importingFile) || forceImportKeyword + ? allowSyntheticDefaults ? ImportKind.Default : ImportKind.Namespace + : ImportKind.CommonJS; + } + // 3. At this point the most correct choice is probably 'import =', but people + // really hate that, so look to see if the importing file has any precedent + // on how to handle it. + for (const statement of importingFile.statements) { + // `import foo` parses as an ImportEqualsDeclaration even though it could be an ImportDeclaration + if (isImportEqualsDeclaration(statement) && !nodeIsMissing(statement.moduleReference)) { + return ImportKind.CommonJS; + } + } + // 4. We have no precedent to go on, so just use a default import if + // allowSyntheticDefaultImports/esModuleInterop is enabled. + return allowSyntheticDefaults ? ImportKind.Default : ImportKind.CommonJS; +} + +function codeActionForFixWorker(changes: ChangeTracker, sourceFile: SourceFile, symbolName: string, fix: ImportFix, includeSymbolNameInDescription: boolean, compilerOptions: CompilerOptions, preferences: UserPreferences): DiagnosticOrDiagnosticAndArguments { + const quotePreference = getQuotePreference(sourceFile, preferences); + switch (fix.kind) { + case ImportFixKind.UseNamespace: + addNamespaceQualifier(changes, sourceFile, fix); + return [Diagnostics.Change_0_to_1, symbolName, `${fix.namespacePrefix}.${symbolName}`]; + case ImportFixKind.JsdocTypeImport: + addImportType(changes, sourceFile, fix, quotePreference); + return [Diagnostics.Change_0_to_1, symbolName, getImportTypePrefix(fix.moduleSpecifier, quotePreference) + symbolName]; + case ImportFixKind.AddToExisting: { + const { importClauseOrBindingPattern, importKind, addAsTypeOnly, moduleSpecifier } = fix; + doAddExistingFix( + changes, + sourceFile, + importClauseOrBindingPattern, + importKind === ImportKind.Default ? { name: symbolName, addAsTypeOnly } : undefined, + importKind === ImportKind.Named ? [{ name: symbolName, addAsTypeOnly }] : emptyArray, + preferences); + const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier); + return includeSymbolNameInDescription + ? [Diagnostics.Import_0_from_1, symbolName, moduleSpecifierWithoutQuotes] + : [Diagnostics.Update_import_from_0, moduleSpecifierWithoutQuotes]; + } + case ImportFixKind.AddNew: { + const { importKind, moduleSpecifier, addAsTypeOnly, useRequire, qualification } = fix; + const getDeclarations = useRequire ? getNewRequires : getNewImports; + const defaultImport: Import | undefined = importKind === ImportKind.Default ? { name: symbolName, addAsTypeOnly } : undefined; + const namedImports: Import[] | undefined = importKind === ImportKind.Named ? [{ name: symbolName, addAsTypeOnly }] : undefined; + const namespaceLikeImport = importKind === ImportKind.Namespace || importKind === ImportKind.CommonJS + ? { importKind, name: qualification?.namespacePrefix || symbolName, addAsTypeOnly } + : undefined; + insertImports(changes, sourceFile, getDeclarations( + moduleSpecifier, + quotePreference, + defaultImport, + namedImports, + namespaceLikeImport, + compilerOptions), /*blankLineBetween*/ true, preferences); + if (qualification) { + addNamespaceQualifier(changes, sourceFile, qualification); + } + return includeSymbolNameInDescription + ? [Diagnostics.Import_0_from_1, symbolName, moduleSpecifier] + : [Diagnostics.Add_import_from_0, moduleSpecifier]; + } + case ImportFixKind.PromoteTypeOnly: { + const { typeOnlyAliasDeclaration } = fix; + const promotedDeclaration = promoteFromTypeOnly(changes, typeOnlyAliasDeclaration, compilerOptions, sourceFile, preferences); + return promotedDeclaration.kind === SyntaxKind.ImportSpecifier + ? [Diagnostics.Remove_type_from_import_of_0_from_1, symbolName, getModuleSpecifierText(promotedDeclaration.parent.parent)] + : [Diagnostics.Remove_type_from_import_declaration_from_0, getModuleSpecifierText(promotedDeclaration)]; + } + default: + return Debug.assertNever(fix, `Unexpected fix kind ${(fix as ImportFix).kind}`); + } +} + +function getModuleSpecifierText(promotedDeclaration: ImportClause | ImportEqualsDeclaration): string { + return promotedDeclaration.kind === SyntaxKind.ImportEqualsDeclaration + ? tryCast(tryCast(promotedDeclaration.moduleReference, isExternalModuleReference)?.expression, isStringLiteralLike)?.text || promotedDeclaration.moduleReference.getText() + : cast(promotedDeclaration.parent.moduleSpecifier, isStringLiteral).text; +} + +function promoteFromTypeOnly(changes: ChangeTracker, aliasDeclaration: TypeOnlyAliasDeclaration, compilerOptions: CompilerOptions, sourceFile: SourceFile, preferences: UserPreferences) { + // See comment in `doAddExistingFix` on constant with the same name. + const convertExistingToTypeOnly = importNameElisionDisabled(compilerOptions); + switch (aliasDeclaration.kind) { + case SyntaxKind.ImportSpecifier: + if (aliasDeclaration.isTypeOnly) { + const sortKind = detectImportSpecifierSorting(aliasDeclaration.parent.elements, preferences); + if (aliasDeclaration.parent.elements.length > 1 && sortKind) { + changes.delete(sourceFile, aliasDeclaration); + const newSpecifier = factory.updateImportSpecifier(aliasDeclaration, /*isTypeOnly*/ false, aliasDeclaration.propertyName, aliasDeclaration.name); + const comparer = getOrganizeImportsComparer(preferences, sortKind === SortKind.CaseInsensitive); + const insertionIndex = getImportSpecifierInsertionIndex(aliasDeclaration.parent.elements, newSpecifier, comparer); + changes.insertImportSpecifierAtIndex(sourceFile, newSpecifier, aliasDeclaration.parent, insertionIndex); + } + else { + changes.deleteRange(sourceFile, aliasDeclaration.getFirstToken()!); + } + return aliasDeclaration; + } + else { + Debug.assert(aliasDeclaration.parent.parent.isTypeOnly); + promoteImportClause(aliasDeclaration.parent.parent); + return aliasDeclaration.parent.parent; + } + case SyntaxKind.ImportClause: + promoteImportClause(aliasDeclaration); + return aliasDeclaration; + case SyntaxKind.NamespaceImport: + promoteImportClause(aliasDeclaration.parent); + return aliasDeclaration.parent; + case SyntaxKind.ImportEqualsDeclaration: + changes.deleteRange(sourceFile, aliasDeclaration.getChildAt(1)); + return aliasDeclaration; + default: + Debug.failBadSyntaxKind(aliasDeclaration); + } + + function promoteImportClause(importClause: ImportClause) { + changes.delete(sourceFile, getTypeKeywordOfTypeOnlyImport(importClause, sourceFile)); + if (convertExistingToTypeOnly) { + const namedImports = tryCast(importClause.namedBindings, isNamedImports); + if (namedImports && namedImports.elements.length > 1) { + if (detectImportSpecifierSorting(namedImports.elements, preferences) && + aliasDeclaration.kind === SyntaxKind.ImportSpecifier && + namedImports.elements.indexOf(aliasDeclaration) !== 0 + ) { + // The import specifier being promoted will be the only non-type-only, + // import in the NamedImports, so it should be moved to the front. + changes.delete(sourceFile, aliasDeclaration); + changes.insertImportSpecifierAtIndex(sourceFile, aliasDeclaration, namedImports, 0); + } + for (const element of namedImports.elements) { + if (element !== aliasDeclaration && !element.isTypeOnly) { + changes.insertModifierBefore(sourceFile, SyntaxKind.TypeKeyword, element); + } + } + } + } + } +} + +function doAddExistingFix( + changes: ChangeTracker, + sourceFile: SourceFile, + clause: ImportClause | ObjectBindingPattern, + defaultImport: Import | undefined, + namedImports: readonly Import[], + preferences: UserPreferences, +): void { + if (clause.kind === SyntaxKind.ObjectBindingPattern) { + if (defaultImport) { + addElementToBindingPattern(clause, defaultImport.name, "default"); + } + for (const specifier of namedImports) { + addElementToBindingPattern(clause, specifier.name, /*propertyName*/ undefined); + } + return; + } + + const promoteFromTypeOnly = clause.isTypeOnly && some([defaultImport, ...namedImports], i => i?.addAsTypeOnly === AddAsTypeOnly.NotAllowed); + const existingSpecifiers = clause.namedBindings && tryCast(clause.namedBindings, isNamedImports)?.elements; + + if (defaultImport) { + Debug.assert(!clause.name, "Cannot add a default import to an import clause that already has one"); + changes.insertNodeAt(sourceFile, clause.getStart(sourceFile), factory.createIdentifier(defaultImport.name), { suffix: ", " }); + } + + if (namedImports.length) { + // sort case sensitivity: + // - if the user preference is explicit, use that + // - otherwise, if there are enough existing import specifiers in this import to detect unambiguously, use that + // - otherwise, detect from other imports in the file + let ignoreCaseForSorting: boolean | undefined; + if (typeof preferences.organizeImportsIgnoreCase === "boolean") { + ignoreCaseForSorting = preferences.organizeImportsIgnoreCase; + } + else if (existingSpecifiers) { + const targetImportSorting = detectImportSpecifierSorting(existingSpecifiers, preferences); + if (targetImportSorting !== SortKind.Both) { + ignoreCaseForSorting = targetImportSorting === SortKind.CaseInsensitive; + } + } + if (ignoreCaseForSorting === undefined) { + ignoreCaseForSorting = detectSorting(sourceFile, preferences) === SortKind.CaseInsensitive; + } + + const comparer = getOrganizeImportsComparer(preferences, ignoreCaseForSorting); + const newSpecifiers = stableSort( + namedImports.map(namedImport => factory.createImportSpecifier( + (!clause.isTypeOnly || promoteFromTypeOnly) && needsTypeOnly(namedImport), + /*propertyName*/ undefined, + factory.createIdentifier(namedImport.name))), + (s1, s2) => compareImportOrExportSpecifiers(s1, s2, comparer)); + + // The sorting preference computed earlier may or may not have validated that these particular + // import specifiers are sorted. If they aren't, `getImportSpecifierInsertionIndex` will return + // nonsense. So if there are existing specifiers, even if we know the sorting preference, we + // need to ensure that the existing specifiers are sorted according to the preference in order + // to do a sorted insertion. + const specifierSort = existingSpecifiers?.length && detectImportSpecifierSorting(existingSpecifiers, preferences); + if (specifierSort && !(ignoreCaseForSorting && specifierSort === SortKind.CaseSensitive)) { + for (const spec of newSpecifiers) { + // Organize imports puts type-only import specifiers last, so if we're + // adding a non-type-only specifier and converting all the other ones to + // type-only, there's no need to ask for the insertion index - it's 0. + const insertionIndex = promoteFromTypeOnly && !spec.isTypeOnly + ? 0 + : getImportSpecifierInsertionIndex(existingSpecifiers, spec, comparer); + changes.insertImportSpecifierAtIndex(sourceFile, spec, clause.namedBindings as NamedImports, insertionIndex); + } + } + else if (existingSpecifiers?.length) { + for (const spec of newSpecifiers) { + changes.insertNodeInListAfter(sourceFile, last(existingSpecifiers), spec, existingSpecifiers); + } + } + else { + if (newSpecifiers.length) { + const namedImports = factory.createNamedImports(newSpecifiers); + if (clause.namedBindings) { + changes.replaceNode(sourceFile, clause.namedBindings, namedImports); + } + else { + changes.insertNodeAfter(sourceFile, Debug.checkDefined(clause.name, "Import clause must have either named imports or a default import"), namedImports); + } + } + } + } + + if (promoteFromTypeOnly) { + changes.delete(sourceFile, getTypeKeywordOfTypeOnlyImport(clause, sourceFile)); + if (existingSpecifiers) { + // We used to convert existing specifiers to type-only only if compiler options indicated that + // would be meaningful (see the `importNameElisionDisabled` utility function), but user + // feedback indicated a preference for preserving the type-onlyness of existing specifiers + // regardless of whether it would make a difference in emit. + for (const specifier of existingSpecifiers) { + changes.insertModifierBefore(sourceFile, SyntaxKind.TypeKeyword, specifier); + } + } + } + + function addElementToBindingPattern(bindingPattern: ObjectBindingPattern, name: string, propertyName: string | undefined) { + const element = factory.createBindingElement(/*dotDotDotToken*/ undefined, propertyName, name); + if (bindingPattern.elements.length) { + changes.insertNodeInListAfter(sourceFile, last(bindingPattern.elements), element); + } + else { + changes.replaceNode(sourceFile, bindingPattern, factory.createObjectBindingPattern([element])); + } + } +} + +function addNamespaceQualifier(changes: ChangeTracker, sourceFile: SourceFile, { namespacePrefix, usagePosition }: Qualification): void { + changes.insertText(sourceFile, usagePosition, namespacePrefix + "."); +} + +function addImportType(changes: ChangeTracker, sourceFile: SourceFile, { moduleSpecifier, usagePosition: position }: FixAddJsdocTypeImport, quotePreference: QuotePreference): void { + changes.insertText(sourceFile, position, getImportTypePrefix(moduleSpecifier, quotePreference)); +} + +function getImportTypePrefix(moduleSpecifier: string, quotePreference: QuotePreference): string { + const quote = getQuoteFromPreference(quotePreference); + return `import(${quote}${moduleSpecifier}${quote}).`; +} + +function needsTypeOnly({ addAsTypeOnly }: { addAsTypeOnly: AddAsTypeOnly }): boolean { + return addAsTypeOnly === AddAsTypeOnly.Required; +} + +function getNewImports( + moduleSpecifier: string, + quotePreference: QuotePreference, + defaultImport: Import | undefined, + namedImports: readonly Import[] | undefined, + namespaceLikeImport: Import & { importKind: ImportKind.CommonJS | ImportKind.Namespace } | undefined, + compilerOptions: CompilerOptions, +): AnyImportSyntax | readonly AnyImportSyntax[] { + const quotedModuleSpecifier = makeStringLiteral(moduleSpecifier, quotePreference); + let statements: AnyImportSyntax | readonly AnyImportSyntax[] | undefined; + if (defaultImport !== undefined || namedImports?.length) { + // `verbatimModuleSyntax` should prefer top-level `import type` - + // even though it's not an error, it would add unnecessary runtime emit. + const topLevelTypeOnly = (!defaultImport || needsTypeOnly(defaultImport)) && every(namedImports, needsTypeOnly) || + compilerOptions.verbatimModuleSyntax && + defaultImport?.addAsTypeOnly !== AddAsTypeOnly.NotAllowed && + !some(namedImports, i => i.addAsTypeOnly === AddAsTypeOnly.NotAllowed); + statements = combine(statements, makeImport( + defaultImport && factory.createIdentifier(defaultImport.name), + namedImports?.map(({ addAsTypeOnly, name }) => factory.createImportSpecifier( + !topLevelTypeOnly && addAsTypeOnly === AddAsTypeOnly.Required, + /*propertyName*/ undefined, + factory.createIdentifier(name))), + moduleSpecifier, + quotePreference, + topLevelTypeOnly)); + } + + if (namespaceLikeImport) { + const declaration = namespaceLikeImport.importKind === ImportKind.CommonJS + ? factory.createImportEqualsDeclaration( + /*modifiers*/ undefined, + needsTypeOnly(namespaceLikeImport), + factory.createIdentifier(namespaceLikeImport.name), + factory.createExternalModuleReference(quotedModuleSpecifier)) + : factory.createImportDeclaration( + /*modifiers*/ undefined, + factory.createImportClause( + needsTypeOnly(namespaceLikeImport), + /*name*/ undefined, + factory.createNamespaceImport(factory.createIdentifier(namespaceLikeImport.name))), + quotedModuleSpecifier, + /*assertClause*/ undefined); + statements = combine(statements, declaration); + } + return Debug.checkDefined(statements); +} + +function getNewRequires(moduleSpecifier: string, quotePreference: QuotePreference, defaultImport: Import | undefined, namedImports: readonly Import[] | undefined, namespaceLikeImport: Import | undefined): RequireVariableStatement | readonly RequireVariableStatement[] { + const quotedModuleSpecifier = makeStringLiteral(moduleSpecifier, quotePreference); + let statements: RequireVariableStatement | readonly RequireVariableStatement[] | undefined; + // const { default: foo, bar, etc } = require('./mod'); + if (defaultImport || namedImports?.length) { + const bindingElements = namedImports?.map(({ name }) => factory.createBindingElement(/*dotDotDotToken*/ undefined, /*propertyName*/ undefined, name)) || []; + if (defaultImport) { + bindingElements.unshift(factory.createBindingElement(/*dotDotDotToken*/ undefined, "default", defaultImport.name)); + } + const declaration = createConstEqualsRequireDeclaration(factory.createObjectBindingPattern(bindingElements), quotedModuleSpecifier); + statements = combine(statements, declaration); + } + // const foo = require('./mod'); + if (namespaceLikeImport) { + const declaration = createConstEqualsRequireDeclaration(namespaceLikeImport.name, quotedModuleSpecifier); + statements = combine(statements, declaration); + } + return Debug.checkDefined(statements); +} + +function createConstEqualsRequireDeclaration(name: string | ObjectBindingPattern, quotedModuleSpecifier: StringLiteral): RequireVariableStatement { + return factory.createVariableStatement( + /*modifiers*/ undefined, + factory.createVariableDeclarationList([ + factory.createVariableDeclaration( + typeof name === "string" ? factory.createIdentifier(name) : name, + /*exclamationToken*/ undefined, + /*type*/ undefined, + factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, [quotedModuleSpecifier]))], + NodeFlags.Const)) as RequireVariableStatement; +} + +function symbolHasMeaning({ declarations }: Symbol, meaning: SemanticMeaning): boolean { + return some(declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)); +} diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 6e7f9916748b7..8adf41aa0421a 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -1,154 +1,18 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; import { - AnyImportOrRequire, - AnyImportOrRequireStatement, - AnyImportSyntax, - arrayFrom, - CancellationToken, - cast, - CodeAction, - CodeFixAction, - CodeFixContextBase, - combine, - compareBooleans, - compareNumberOfDirectorySeparators, - compareValues, - Comparison, - CompilerOptions, - createModuleSpecifierResolutionHost, - createMultiMap, - createPackageJsonImportFilter, - Debug, - DiagnosticOrDiagnosticAndArguments, - Diagnostics, - DiagnosticWithLocation, - emptyArray, - every, - ExportKind, - factory, - first, - firstDefined, - flatMap, - flatMapIterator, - forEachExternalModuleToImportFrom, - formatting, - getAllowSyntheticDefaultImports, - getBaseFileName, - getDefaultExportInfoWorker, - getDefaultLikeExportInfo, - getDirectoryPath, - getEmitModuleKind, - getEmitModuleResolutionKind, - getEmitScriptTarget, - getExportInfoMap, - getMeaningFromDeclaration, - getMeaningFromLocation, - getNameForExportedSymbol, - getNodeId, - getQuoteFromPreference, - getQuotePreference, - getSourceFileOfNode, - getSymbolId, - getTokenAtPosition, - getTypeKeywordOfTypeOnlyImport, - getUniqueSymbolId, - hostGetCanonicalFileName, - Identifier, - ImportClause, - ImportEqualsDeclaration, - importFromModuleSpecifier, - ImportKind, - importNameElisionDisabled, - ImportsNotUsedAsValues, - insertImports, - InternalSymbolName, - isExternalModule, - isExternalModuleReference, - isIdentifier, - isIdentifierPart, - isIdentifierStart, - isImportableFile, - isImportEqualsDeclaration, - isInJSFile, - isIntrinsicJsxName, - isJsxClosingElement, - isJsxOpeningFragment, - isJsxOpeningLikeElement, - isJSXTagName, - isNamedImports, - isNamespaceImport, - isSourceFileJS, - isStringANonContextualKeyword, - isStringLiteral, - isStringLiteralLike, - isTypeOnlyImportDeclaration, - isTypeOnlyImportOrExportDeclaration, - isUMDExportSymbol, - isValidTypeOnlyAliasUseSite, - isVariableDeclarationInitializedToRequire, - jsxModeNeedsExplicitImport, - LanguageServiceHost, - last, - makeImport, - makeStringLiteral, - mapDefined, - memoizeOne, - ModuleKind, - ModuleResolutionKind, - moduleResolutionUsesNodeModules, - moduleSpecifiers, - MultiMap, - Mutable, - NamedImports, - Node, - NodeFlags, - nodeIsMissing, - ObjectBindingPattern, - OrganizeImports, - PackageJsonImportFilter, - Path, - pathContainsNodeModules, - pathIsBareSpecifier, - Program, - QuotePreference, - removeFileExtension, - removeSuffix, - RequireVariableStatement, - ScriptTarget, - SemanticMeaning, - shouldUseUriStyleNodeCoreModules, - single, - skipAlias, - some, - sort, - SortKind, - SourceFile, - stableSort, - startsWith, - StringLiteral, - stripQuotes, - Symbol, - SymbolExportInfo, - SymbolFlags, - SymbolId, - SyntaxKind, - textChanges, - toPath, - tryCast, - tryGetModuleSpecifierFromDeclaration, - TypeChecker, - TypeOnlyAliasDeclaration, - UserPreferences, -} from "../_namespaces/ts"; -import { - createCodeFixAction, createCombinedCodeActions, eachDiagnostic, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + codeActionForFix, + createImportAdder, + fixId, + fixName, + getFixInfos, +} from "./importAdder"; -/** @internal */ -export const importFixName = "import"; -const importFixId = "fixMissingImport"; const errorCodes: readonly number[] = [ Diagnostics.Cannot_find_name_0.code, Diagnostics.Cannot_find_name_0_Did_you_mean_1.code, @@ -161,1471 +25,23 @@ const errorCodes: readonly number[] = [ Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type.code, ]; +/** @internal */ +export const importFixName = fixName; + registerCodeFix({ errorCodes, getCodeActions(context) { const { errorCode, preferences, sourceFile, span, program } = context; const info = getFixInfos(context, errorCode, span.start, /*useAutoImportProvider*/ true); if (!info) return undefined; - return info.map(({ fix, symbolName, errorIdentifierText }) => codeActionForFix( - context, - sourceFile, - symbolName, - fix, - /*includeSymbolNameInDescription*/ symbolName !== errorIdentifierText, - program.getCompilerOptions(), - preferences)); + return info.map(({ fix, symbolName, errorIdentifierText }) => + codeActionForFix(context, sourceFile, symbolName, fix, /*includeSymbolNameInDescription*/ symbolName !== errorIdentifierText, program.getCompilerOptions(), preferences)); }, - fixIds: [importFixId], + fixIds: [fixId], getAllCodeActions: context => { const { sourceFile, program, preferences, host, cancellationToken } = context; - const importAdder = createImportAdderWorker(sourceFile, program, /*useAutoImportProvider*/ true, preferences, host, cancellationToken); + const importAdder = createImportAdder(sourceFile, program, preferences, host, /*useAutoImportProvider*/ true, cancellationToken); eachDiagnostic(context, errorCodes, diag => importAdder.addImportFromDiagnostic(diag, context)); - return createCombinedCodeActions(textChanges.ChangeTracker.with(context, importAdder.writeFixes)); + return createCombinedCodeActions(ChangeTracker.with(context, importAdder.writeFixes)); }, }); - -/** - * Computes multiple import additions to a file and writes them to a ChangeTracker. - * - * @internal - */ -export interface ImportAdder { - hasFixes(): boolean; - addImportFromDiagnostic: (diagnostic: DiagnosticWithLocation, context: CodeFixContextBase) => void; - addImportFromExportedSymbol: (exportedSymbol: Symbol, isValidTypeOnlyUseSite?: boolean) => void; - writeFixes: (changeTracker: textChanges.ChangeTracker) => void; -} - -/** @internal */ -export function createImportAdder(sourceFile: SourceFile, program: Program, preferences: UserPreferences, host: LanguageServiceHost, cancellationToken?: CancellationToken): ImportAdder { - return createImportAdderWorker(sourceFile, program, /*useAutoImportProvider*/ false, preferences, host, cancellationToken); -} - -interface AddToExistingState { - readonly importClauseOrBindingPattern: ImportClause | ObjectBindingPattern; - defaultImport: Import | undefined; - readonly namedImports: Map; -} - -function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAutoImportProvider: boolean, preferences: UserPreferences, host: LanguageServiceHost, cancellationToken: CancellationToken | undefined): ImportAdder { - const compilerOptions = program.getCompilerOptions(); - // Namespace fixes don't conflict, so just build a list. - const addToNamespace: FixUseNamespaceImport[] = []; - const importType: FixAddJsdocTypeImport[] = []; - /** Keys are import clause node IDs. */ - const addToExisting = new Map(); - - type NewImportsKey = `${0 | 1}|${string}`; - /** Use `getNewImportEntry` for access */ - const newImports = new Map>(); - return { addImportFromDiagnostic, addImportFromExportedSymbol, writeFixes, hasFixes }; - - function addImportFromDiagnostic(diagnostic: DiagnosticWithLocation, context: CodeFixContextBase) { - const info = getFixInfos(context, diagnostic.code, diagnostic.start, useAutoImportProvider); - if (!info || !info.length) return; - addImport(first(info)); - } - - function addImportFromExportedSymbol(exportedSymbol: Symbol, isValidTypeOnlyUseSite?: boolean) { - const moduleSymbol = Debug.checkDefined(exportedSymbol.parent); - const symbolName = getNameForExportedSymbol(exportedSymbol, getEmitScriptTarget(compilerOptions)); - const checker = program.getTypeChecker(); - const symbol = checker.getMergedSymbol(skipAlias(exportedSymbol, checker)); - const exportInfo = getAllExportInfoForSymbol(sourceFile, symbol, symbolName, moduleSymbol, /*preferCapitalized*/ false, program, host, preferences, cancellationToken); - const useRequire = shouldUseRequire(sourceFile, program); - const fix = getImportFixForSymbol(sourceFile, Debug.checkDefined(exportInfo), program, /*position*/ undefined, !!isValidTypeOnlyUseSite, useRequire, host, preferences); - if (fix) { - addImport({ fix, symbolName, errorIdentifierText: undefined }); - } - } - - function addImport(info: FixInfo) { - const { fix, symbolName } = info; - switch (fix.kind) { - case ImportFixKind.UseNamespace: - addToNamespace.push(fix); - break; - case ImportFixKind.JsdocTypeImport: - importType.push(fix); - break; - case ImportFixKind.AddToExisting: { - const { importClauseOrBindingPattern, importKind, addAsTypeOnly } = fix; - const key = String(getNodeId(importClauseOrBindingPattern)); - let entry = addToExisting.get(key); - if (!entry) { - addToExisting.set(key, entry = { importClauseOrBindingPattern, defaultImport: undefined, namedImports: new Map() }); - } - if (importKind === ImportKind.Named) { - const prevValue = entry?.namedImports.get(symbolName); - entry.namedImports.set(symbolName, reduceAddAsTypeOnlyValues(prevValue, addAsTypeOnly)); - } - else { - Debug.assert(entry.defaultImport === undefined || entry.defaultImport.name === symbolName, "(Add to Existing) Default import should be missing or match symbolName"); - entry.defaultImport = { - name: symbolName, - addAsTypeOnly: reduceAddAsTypeOnlyValues(entry.defaultImport?.addAsTypeOnly, addAsTypeOnly), - }; - } - break; - } - case ImportFixKind.AddNew: { - const { moduleSpecifier, importKind, useRequire, addAsTypeOnly } = fix; - const entry = getNewImportEntry(moduleSpecifier, importKind, useRequire, addAsTypeOnly); - Debug.assert(entry.useRequire === useRequire, "(Add new) Tried to add an `import` and a `require` for the same module"); - - switch (importKind) { - case ImportKind.Default: - Debug.assert(entry.defaultImport === undefined || entry.defaultImport.name === symbolName, "(Add new) Default import should be missing or match symbolName"); - entry.defaultImport = { name: symbolName, addAsTypeOnly: reduceAddAsTypeOnlyValues(entry.defaultImport?.addAsTypeOnly, addAsTypeOnly) }; - break; - case ImportKind.Named: - const prevValue = (entry.namedImports ||= new Map()).get(symbolName); - entry.namedImports.set(symbolName, reduceAddAsTypeOnlyValues(prevValue, addAsTypeOnly)); - break; - case ImportKind.CommonJS: - case ImportKind.Namespace: - Debug.assert(entry.namespaceLikeImport === undefined || entry.namespaceLikeImport.name === symbolName, "Namespacelike import shoudl be missing or match symbolName"); - entry.namespaceLikeImport = { importKind, name: symbolName, addAsTypeOnly }; - break; - } - break; - } - case ImportFixKind.PromoteTypeOnly: - // Excluding from fix-all - break; - default: - Debug.assertNever(fix, `fix wasn't never - got kind ${(fix as ImportFix).kind}`); - } - - function reduceAddAsTypeOnlyValues(prevValue: AddAsTypeOnly | undefined, newValue: AddAsTypeOnly): AddAsTypeOnly { - // `NotAllowed` overrides `Required` because one addition of a new import might be required to be type-only - // because of `--importsNotUsedAsValues=error`, but if a second addition of the same import is `NotAllowed` - // to be type-only, the reason the first one was `Required` - the unused runtime dependency - is now moot. - // Alternatively, if one addition is `Required` because it has no value meaning under `--preserveValueImports` - // and `--isolatedModules`, it should be impossible for another addition to be `NotAllowed` since that would - // mean a type is being referenced in a value location. - return Math.max(prevValue ?? 0, newValue); - } - - function getNewImportEntry(moduleSpecifier: string, importKind: ImportKind, useRequire: boolean, addAsTypeOnly: AddAsTypeOnly): Mutable { - // A default import that requires type-only makes the whole import type-only. - // (We could add `default` as a named import, but that style seems undesirable.) - // Under `--preserveValueImports` and `--importsNotUsedAsValues=error`, if a - // module default-exports a type but named-exports some values (weird), you would - // have to use a type-only default import and non-type-only named imports. These - // require two separate import declarations, so we build this into the map key. - const typeOnlyKey = newImportsKey(moduleSpecifier, /*topLevelTypeOnly*/ true); - const nonTypeOnlyKey = newImportsKey(moduleSpecifier, /*topLevelTypeOnly*/ false); - const typeOnlyEntry = newImports.get(typeOnlyKey); - const nonTypeOnlyEntry = newImports.get(nonTypeOnlyKey); - const newEntry: ImportsCollection & { useRequire: boolean } = { - defaultImport: undefined, - namedImports: undefined, - namespaceLikeImport: undefined, - useRequire - }; - if (importKind === ImportKind.Default && addAsTypeOnly === AddAsTypeOnly.Required) { - if (typeOnlyEntry) return typeOnlyEntry; - newImports.set(typeOnlyKey, newEntry); - return newEntry; - } - if (addAsTypeOnly === AddAsTypeOnly.Allowed && (typeOnlyEntry || nonTypeOnlyEntry)) { - return (typeOnlyEntry || nonTypeOnlyEntry)!; - } - if (nonTypeOnlyEntry) { - return nonTypeOnlyEntry; - } - newImports.set(nonTypeOnlyKey, newEntry); - return newEntry; - } - - function newImportsKey(moduleSpecifier: string, topLevelTypeOnly: boolean): NewImportsKey { - return `${topLevelTypeOnly ? 1 : 0}|${moduleSpecifier}`; - } - } - - function writeFixes(changeTracker: textChanges.ChangeTracker) { - const quotePreference = getQuotePreference(sourceFile, preferences); - for (const fix of addToNamespace) { - addNamespaceQualifier(changeTracker, sourceFile, fix); - } - for (const fix of importType) { - addImportType(changeTracker, sourceFile, fix, quotePreference); - } - addToExisting.forEach(({ importClauseOrBindingPattern, defaultImport, namedImports }) => { - doAddExistingFix( - changeTracker, - sourceFile, - importClauseOrBindingPattern, - defaultImport, - arrayFrom(namedImports.entries(), ([name, addAsTypeOnly]) => ({ addAsTypeOnly, name })), - preferences); - }); - - let newDeclarations: AnyImportOrRequireStatement | readonly AnyImportOrRequireStatement[] | undefined; - newImports.forEach(({ useRequire, defaultImport, namedImports, namespaceLikeImport }, key) => { - const moduleSpecifier = key.slice(2); // From `${0 | 1}|${moduleSpecifier}` format - const getDeclarations = useRequire ? getNewRequires : getNewImports; - const declarations = getDeclarations( - moduleSpecifier, - quotePreference, - defaultImport, - namedImports && arrayFrom(namedImports.entries(), ([name, addAsTypeOnly]) => ({ addAsTypeOnly, name })), - namespaceLikeImport, - compilerOptions); - newDeclarations = combine(newDeclarations, declarations); - }); - if (newDeclarations) { - insertImports(changeTracker, sourceFile, newDeclarations, /*blankLineBetween*/ true, preferences); - } - } - - function hasFixes() { - return addToNamespace.length > 0 || importType.length > 0 || addToExisting.size > 0 || newImports.size > 0; - } -} - -/** - * Computes module specifiers for multiple import additions to a file. - * - * @internal - */ - export interface ImportSpecifierResolver { - getModuleSpecifierForBestExportInfo( - exportInfo: readonly SymbolExportInfo[], - position: number, - isValidTypeOnlyUseSite: boolean, - fromCacheOnly?: boolean - ): { exportInfo?: SymbolExportInfo, moduleSpecifier: string, computedWithoutCacheCount: number } | undefined; -} - -/** @internal */ -export function createImportSpecifierResolver(importingFile: SourceFile, program: Program, host: LanguageServiceHost, preferences: UserPreferences): ImportSpecifierResolver { - const packageJsonImportFilter = createPackageJsonImportFilter(importingFile, preferences, host); - const importMap = createExistingImportMap(program.getTypeChecker(), importingFile, program.getCompilerOptions()); - return { getModuleSpecifierForBestExportInfo }; - - function getModuleSpecifierForBestExportInfo( - exportInfo: readonly SymbolExportInfo[], - position: number, - isValidTypeOnlyUseSite: boolean, - fromCacheOnly?: boolean, - ): { exportInfo?: SymbolExportInfo, moduleSpecifier: string, computedWithoutCacheCount: number } | undefined { - const { fixes, computedWithoutCacheCount } = getImportFixes( - exportInfo, - position, - isValidTypeOnlyUseSite, - /*useRequire*/ false, - program, - importingFile, - host, - preferences, - importMap, - fromCacheOnly); - const result = getBestFix(fixes, importingFile, program, packageJsonImportFilter, host); - return result && { ...result, computedWithoutCacheCount }; - } -} - -// Sorted with the preferred fix coming first. -const enum ImportFixKind { UseNamespace, JsdocTypeImport, AddToExisting, AddNew, PromoteTypeOnly } -// These should not be combined as bitflags, but are given powers of 2 values to -// easily detect conflicts between `NotAllowed` and `Required` by giving them a unique sum. -// They're also ordered in terms of increasing priority for a fix-all scenario (see -// `reduceAddAsTypeOnlyValues`). -const enum AddAsTypeOnly { - Allowed = 1 << 0, - Required = 1 << 1, - NotAllowed = 1 << 2, -} -type ImportFix = FixUseNamespaceImport | FixAddJsdocTypeImport | FixAddToExistingImport | FixAddNewImport | FixPromoteTypeOnlyImport; -type ImportFixWithModuleSpecifier = FixUseNamespaceImport | FixAddJsdocTypeImport | FixAddToExistingImport | FixAddNewImport; - -// Properties are be undefined if fix is derived from an existing import -interface ImportFixBase { - readonly isReExport?: boolean; - readonly exportInfo?: SymbolExportInfo; - readonly moduleSpecifier: string; -} -interface Qualification { - readonly usagePosition: number; - readonly namespacePrefix: string; -} -interface FixUseNamespaceImport extends ImportFixBase, Qualification { - readonly kind: ImportFixKind.UseNamespace; -} -interface FixAddJsdocTypeImport extends ImportFixBase { - readonly kind: ImportFixKind.JsdocTypeImport; - readonly usagePosition: number; - readonly isReExport: boolean; - readonly exportInfo: SymbolExportInfo; -} -interface FixAddToExistingImport extends ImportFixBase { - readonly kind: ImportFixKind.AddToExisting; - readonly importClauseOrBindingPattern: ImportClause | ObjectBindingPattern; - readonly importKind: ImportKind.Default | ImportKind.Named; - readonly addAsTypeOnly: AddAsTypeOnly; -} -interface FixAddNewImport extends ImportFixBase { - readonly kind: ImportFixKind.AddNew; - readonly importKind: ImportKind; - readonly addAsTypeOnly: AddAsTypeOnly; - readonly useRequire: boolean; - readonly qualification?: Qualification; -} -interface FixPromoteTypeOnlyImport { - readonly kind: ImportFixKind.PromoteTypeOnly; - readonly typeOnlyAliasDeclaration: TypeOnlyAliasDeclaration; -} - - -/** Information needed to augment an existing import declaration. */ -interface FixAddToExistingImportInfo { - readonly declaration: AnyImportOrRequire; - readonly importKind: ImportKind; - readonly targetFlags: SymbolFlags; - readonly symbol: Symbol; -} - -/** @internal */ -export function getImportCompletionAction( - targetSymbol: Symbol, - moduleSymbol: Symbol, - exportMapKey: string | undefined, - sourceFile: SourceFile, - symbolName: string, - isJsxTagName: boolean, - host: LanguageServiceHost, - program: Program, - formatContext: formatting.FormatContext, - position: number, - preferences: UserPreferences, - cancellationToken: CancellationToken, -): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } { - const compilerOptions = program.getCompilerOptions(); - let exportInfos; - - if (exportMapKey) { - // The new way: `exportMapKey` should be in the `data` of each auto-import completion entry and - // sent back when asking for details. - exportInfos = getExportInfoMap(sourceFile, host, program, preferences, cancellationToken).get(sourceFile.path, exportMapKey); - Debug.assertIsDefined(exportInfos, "Some exportInfo should match the specified exportMapKey"); - } - else { - // The old way, kept alive for super old editors that don't give us `data` back. - exportInfos = pathIsBareSpecifier(stripQuotes(moduleSymbol.name)) - ? [getSingleExportInfoForSymbol(targetSymbol, symbolName, moduleSymbol, program, host)] - : getAllExportInfoForSymbol(sourceFile, targetSymbol, symbolName, moduleSymbol, isJsxTagName, program, host, preferences, cancellationToken); - Debug.assertIsDefined(exportInfos, "Some exportInfo should match the specified symbol / moduleSymbol"); - } - - const useRequire = shouldUseRequire(sourceFile, program); - const isValidTypeOnlyUseSite = isValidTypeOnlyAliasUseSite(getTokenAtPosition(sourceFile, position)); - const fix = Debug.checkDefined(getImportFixForSymbol(sourceFile, exportInfos, program, position, isValidTypeOnlyUseSite, useRequire, host, preferences)); - return { - moduleSpecifier: fix.moduleSpecifier, - codeAction: codeFixActionToCodeAction(codeActionForFix( - { host, formatContext, preferences }, - sourceFile, - symbolName, - fix, - /*includeSymbolNameInDescription*/ false, - compilerOptions, - preferences)) - }; -} - -/** @internal */ -export function getPromoteTypeOnlyCompletionAction(sourceFile: SourceFile, symbolToken: Identifier, program: Program, host: LanguageServiceHost, formatContext: formatting.FormatContext, preferences: UserPreferences) { - const compilerOptions = program.getCompilerOptions(); - const symbolName = single(getSymbolNamesToImport(sourceFile, program.getTypeChecker(), symbolToken, compilerOptions)); - const fix = getTypeOnlyPromotionFix(sourceFile, symbolToken, symbolName, program); - const includeSymbolNameInDescription = symbolName !== symbolToken.text; - return fix && codeFixActionToCodeAction(codeActionForFix({ host, formatContext, preferences }, sourceFile, symbolName, fix, includeSymbolNameInDescription, compilerOptions, preferences)); -} - -function getImportFixForSymbol(sourceFile: SourceFile, exportInfos: readonly SymbolExportInfo[], program: Program, position: number | undefined, isValidTypeOnlyUseSite: boolean, useRequire: boolean, host: LanguageServiceHost, preferences: UserPreferences) { - const packageJsonImportFilter = createPackageJsonImportFilter(sourceFile, preferences, host); - return getBestFix(getImportFixes(exportInfos, position, isValidTypeOnlyUseSite, useRequire, program, sourceFile, host, preferences).fixes, sourceFile, program, packageJsonImportFilter, host); -} - -function codeFixActionToCodeAction({ description, changes, commands }: CodeFixAction): CodeAction { - return { description, changes, commands }; -} - -function getAllExportInfoForSymbol(importingFile: SourceFile, symbol: Symbol, symbolName: string, moduleSymbol: Symbol, preferCapitalized: boolean, program: Program, host: LanguageServiceHost, preferences: UserPreferences, cancellationToken: CancellationToken | undefined): readonly SymbolExportInfo[] | undefined { - const getChecker = createGetChecker(program, host); - return getExportInfoMap(importingFile, host, program, preferences, cancellationToken) - .search(importingFile.path, preferCapitalized, name => name === symbolName, info => { - if (skipAlias(info[0].symbol, getChecker(info[0].isFromPackageJson)) === symbol && info.some(i => i.moduleSymbol === moduleSymbol || i.symbol.parent === moduleSymbol)) { - return info; - } - }); -} - -function getSingleExportInfoForSymbol(symbol: Symbol, symbolName: string, moduleSymbol: Symbol, program: Program, host: LanguageServiceHost): SymbolExportInfo { - const compilerOptions = program.getCompilerOptions(); - const mainProgramInfo = getInfoWithChecker(program.getTypeChecker(), /*isFromPackageJson*/ false); - if (mainProgramInfo) { - return mainProgramInfo; - } - const autoImportProvider = host.getPackageJsonAutoImportProvider?.()?.getTypeChecker(); - return Debug.checkDefined(autoImportProvider && getInfoWithChecker(autoImportProvider, /*isFromPackageJson*/ true), `Could not find symbol in specified module for code actions`); - - function getInfoWithChecker(checker: TypeChecker, isFromPackageJson: boolean): SymbolExportInfo | undefined { - const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions); - if (defaultInfo && skipAlias(defaultInfo.symbol, checker) === symbol) { - return { symbol: defaultInfo.symbol, moduleSymbol, moduleFileName: undefined, exportKind: defaultInfo.exportKind, targetFlags: skipAlias(symbol, checker).flags, isFromPackageJson }; - } - const named = checker.tryGetMemberInModuleExportsAndProperties(symbolName, moduleSymbol); - if (named && skipAlias(named, checker) === symbol) { - return { symbol: named, moduleSymbol, moduleFileName: undefined, exportKind: ExportKind.Named, targetFlags: skipAlias(symbol, checker).flags, isFromPackageJson }; - } - } -} - -function getImportFixes( - exportInfos: readonly SymbolExportInfo[], - usagePosition: number | undefined, - isValidTypeOnlyUseSite: boolean, - useRequire: boolean, - program: Program, - sourceFile: SourceFile, - host: LanguageServiceHost, - preferences: UserPreferences, - importMap = createExistingImportMap(program.getTypeChecker(), sourceFile, program.getCompilerOptions()), - fromCacheOnly?: boolean, -): { computedWithoutCacheCount: number, fixes: readonly ImportFixWithModuleSpecifier[] } { - const checker = program.getTypeChecker(); - const existingImports = flatMap(exportInfos, importMap.getImportsForExportInfo); - const useNamespace = usagePosition !== undefined && tryUseExistingNamespaceImport(existingImports, usagePosition); - const addToExisting = tryAddToExistingImport(existingImports, isValidTypeOnlyUseSite, checker, program.getCompilerOptions()); - if (addToExisting) { - // Don't bother providing an action to add a new import if we can add to an existing one. - return { - computedWithoutCacheCount: 0, - fixes: [...(useNamespace ? [useNamespace] : emptyArray), addToExisting], - }; - } - - const { fixes, computedWithoutCacheCount = 0 } = getFixesForAddImport( - exportInfos, - existingImports, - program, - sourceFile, - usagePosition, - isValidTypeOnlyUseSite, - useRequire, - host, - preferences, - fromCacheOnly); - return { - computedWithoutCacheCount, - fixes: [...(useNamespace ? [useNamespace] : emptyArray), ...fixes], - }; -} - -function tryUseExistingNamespaceImport(existingImports: readonly FixAddToExistingImportInfo[], position: number): FixUseNamespaceImport | undefined { - // 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. - return firstDefined(existingImports, ({ declaration, importKind }): FixUseNamespaceImport | undefined => { - if (importKind !== ImportKind.Named) return undefined; - const namespacePrefix = getNamespaceLikeImportText(declaration); - const moduleSpecifier = namespacePrefix && tryGetModuleSpecifierFromDeclaration(declaration)?.text; - if (moduleSpecifier) { - return { kind: ImportFixKind.UseNamespace, namespacePrefix, usagePosition: position, moduleSpecifier }; - } - }); -} - -function getNamespaceLikeImportText(declaration: AnyImportOrRequire) { - switch (declaration.kind) { - case SyntaxKind.VariableDeclaration: - return tryCast(declaration.name, isIdentifier)?.text; - case SyntaxKind.ImportEqualsDeclaration: - return declaration.name.text; - case SyntaxKind.ImportDeclaration: - return tryCast(declaration.importClause?.namedBindings, isNamespaceImport)?.name.text; - default: - return Debug.assertNever(declaration); - } -} - -function getAddAsTypeOnly( - isValidTypeOnlyUseSite: boolean, - isForNewImportDeclaration: boolean, - symbol: Symbol, - targetFlags: SymbolFlags, - checker: TypeChecker, - compilerOptions: CompilerOptions -) { - if (!isValidTypeOnlyUseSite) { - // Can't use a type-only import if the usage is an emitting position - return AddAsTypeOnly.NotAllowed; - } - if (isForNewImportDeclaration && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error) { - // Not writing a (top-level) type-only import here would create an error because the runtime dependency is unnecessary - return AddAsTypeOnly.Required; - } - if (importNameElisionDisabled(compilerOptions) && - (!(targetFlags & SymbolFlags.Value) || !!checker.getTypeOnlyAliasDeclaration(symbol)) - ) { - // A type-only import is required for this symbol if under these settings if the symbol will - // be erased, which will happen if the target symbol is purely a type or if it was exported/imported - // as type-only already somewhere between this import and the target. - return AddAsTypeOnly.Required; - } - return AddAsTypeOnly.Allowed; -} - -function tryAddToExistingImport(existingImports: readonly FixAddToExistingImportInfo[], isValidTypeOnlyUseSite: boolean, checker: TypeChecker, compilerOptions: CompilerOptions): FixAddToExistingImport | undefined { - let best: FixAddToExistingImport | undefined; - for (const existingImport of existingImports) { - const fix = getAddToExistingImportFix(existingImport); - if (!fix) continue; - const isTypeOnly = isTypeOnlyImportDeclaration(fix.importClauseOrBindingPattern); - if ( - fix.addAsTypeOnly !== AddAsTypeOnly.NotAllowed && isTypeOnly || - fix.addAsTypeOnly === AddAsTypeOnly.NotAllowed && !isTypeOnly - ) { - // Give preference to putting types in existing type-only imports and avoiding conversions - // of import statements to/from type-only. - return fix; - } - best ??= fix; - } - return best; - - function getAddToExistingImportFix({ declaration, importKind, symbol, targetFlags }: FixAddToExistingImportInfo): FixAddToExistingImport | undefined { - if (importKind === ImportKind.CommonJS || importKind === ImportKind.Namespace || declaration.kind === SyntaxKind.ImportEqualsDeclaration) { - // These kinds of imports are not combinable with anything - return undefined; - } - - if (declaration.kind === SyntaxKind.VariableDeclaration) { - return (importKind === ImportKind.Named || importKind === ImportKind.Default) && declaration.name.kind === SyntaxKind.ObjectBindingPattern - ? { kind: ImportFixKind.AddToExisting, importClauseOrBindingPattern: declaration.name, importKind, moduleSpecifier: declaration.initializer.arguments[0].text, addAsTypeOnly: AddAsTypeOnly.NotAllowed } - : undefined; - } - - const { importClause } = declaration; - if (!importClause || !isStringLiteralLike(declaration.moduleSpecifier)) { - return undefined; - } - const { name, namedBindings } = importClause; - // A type-only import may not have both a default and named imports, so the only way a name can - // be added to an existing type-only import is adding a named import to existing named bindings. - if (importClause.isTypeOnly && !(importKind === ImportKind.Named && namedBindings)) { - return undefined; - } - - // N.B. we don't have to figure out whether to use the main program checker - // or the AutoImportProvider checker because we're adding to an existing import; the existence of - // the import guarantees the symbol came from the main program. - const addAsTypeOnly = getAddAsTypeOnly(isValidTypeOnlyUseSite, /*isForNewImportDeclaration*/ false, symbol, targetFlags, checker, compilerOptions); - - if (importKind === ImportKind.Default && ( - name || // Cannot add a default import to a declaration that already has one - addAsTypeOnly === AddAsTypeOnly.Required && namedBindings // Cannot add a default import as type-only if the import already has named bindings - )) { - return undefined; - } - if (importKind === ImportKind.Named && - namedBindings?.kind === SyntaxKind.NamespaceImport // Cannot add a named import to a declaration that has a namespace import - ) { - return undefined; - } - - return { - kind: ImportFixKind.AddToExisting, - importClauseOrBindingPattern: importClause, - importKind, - moduleSpecifier: declaration.moduleSpecifier.text, - addAsTypeOnly, - }; - } -} - -function createExistingImportMap(checker: TypeChecker, importingFile: SourceFile, compilerOptions: CompilerOptions) { - let importMap: MultiMap | undefined; - for (const moduleSpecifier of importingFile.imports) { - const i = importFromModuleSpecifier(moduleSpecifier); - if (isVariableDeclarationInitializedToRequire(i.parent)) { - const moduleSymbol = checker.resolveExternalModuleName(moduleSpecifier); - if (moduleSymbol) { - (importMap ||= createMultiMap()).add(getSymbolId(moduleSymbol), i.parent); - } - } - else if (i.kind === SyntaxKind.ImportDeclaration || i.kind === SyntaxKind.ImportEqualsDeclaration) { - const moduleSymbol = checker.getSymbolAtLocation(moduleSpecifier); - if (moduleSymbol) { - (importMap ||= createMultiMap()).add(getSymbolId(moduleSymbol), i); - } - } - } - - return { - getImportsForExportInfo: ({ moduleSymbol, exportKind, targetFlags, symbol }: SymbolExportInfo): readonly FixAddToExistingImportInfo[] => { - // Can't use an es6 import for a type in JS. - if (!(targetFlags & SymbolFlags.Value) && isSourceFileJS(importingFile)) return emptyArray; - const matchingDeclarations = importMap?.get(getSymbolId(moduleSymbol)); - if (!matchingDeclarations) return emptyArray; - const importKind = getImportKind(importingFile, exportKind, compilerOptions); - return matchingDeclarations.map(declaration => ({ declaration, importKind, symbol, targetFlags })); - } - }; -} - -function shouldUseRequire(sourceFile: SourceFile, program: Program): boolean { - // 1. TypeScript files don't use require variable declarations - if (!isSourceFileJS(sourceFile)) { - return false; - } - - // 2. If the current source file is unambiguously CJS or ESM, go with that - if (sourceFile.commonJsModuleIndicator && !sourceFile.externalModuleIndicator) return true; - if (sourceFile.externalModuleIndicator && !sourceFile.commonJsModuleIndicator) return false; - - // 3. If there's a tsconfig/jsconfig, use its module setting - const compilerOptions = program.getCompilerOptions(); - if (compilerOptions.configFile) { - return getEmitModuleKind(compilerOptions) < ModuleKind.ES2015; - } - - // 4. Match the first other JS file in the program that's unambiguously CJS or ESM - for (const otherFile of program.getSourceFiles()) { - if (otherFile === sourceFile || !isSourceFileJS(otherFile) || program.isSourceFileFromExternalLibrary(otherFile)) continue; - if (otherFile.commonJsModuleIndicator && !otherFile.externalModuleIndicator) return true; - if (otherFile.externalModuleIndicator && !otherFile.commonJsModuleIndicator) return false; - } - - // 5. Literally nothing to go on - return true; -} - -function createGetChecker(program: Program, host: LanguageServiceHost) { - return memoizeOne((isFromPackageJson: boolean) => isFromPackageJson ? host.getPackageJsonAutoImportProvider!()!.getTypeChecker() : program.getTypeChecker()); -} - -function getNewImportFixes( - program: Program, - sourceFile: SourceFile, - usagePosition: number | undefined, - isValidTypeOnlyUseSite: boolean, - useRequire: boolean, - exportInfo: readonly SymbolExportInfo[], - host: LanguageServiceHost, - preferences: UserPreferences, - fromCacheOnly?: boolean, -): { computedWithoutCacheCount: number, fixes: readonly (FixAddNewImport | FixAddJsdocTypeImport)[] } { - const isJs = isSourceFileJS(sourceFile); - const compilerOptions = program.getCompilerOptions(); - const moduleSpecifierResolutionHost = createModuleSpecifierResolutionHost(program, host); - const getChecker = createGetChecker(program, host); - const moduleResolution = getEmitModuleResolutionKind(compilerOptions); - const rejectNodeModulesRelativePaths = moduleResolutionUsesNodeModules(moduleResolution); - const getModuleSpecifiers = fromCacheOnly - ? (moduleSymbol: Symbol) => ({ moduleSpecifiers: moduleSpecifiers.tryGetModuleSpecifiersFromCache(moduleSymbol, sourceFile, moduleSpecifierResolutionHost, preferences), computedWithoutCache: false }) - : (moduleSymbol: Symbol, checker: TypeChecker) => moduleSpecifiers.getModuleSpecifiersWithCacheInfo(moduleSymbol, checker, compilerOptions, sourceFile, moduleSpecifierResolutionHost, preferences); - - let computedWithoutCacheCount = 0; - const fixes = flatMap(exportInfo, (exportInfo, i) => { - const checker = getChecker(exportInfo.isFromPackageJson); - const { computedWithoutCache, moduleSpecifiers } = getModuleSpecifiers(exportInfo.moduleSymbol, checker); - const importedSymbolHasValueMeaning = !!(exportInfo.targetFlags & SymbolFlags.Value); - const addAsTypeOnly = getAddAsTypeOnly(isValidTypeOnlyUseSite, /*isForNewImportDeclaration*/ true, exportInfo.symbol, exportInfo.targetFlags, checker, compilerOptions); - computedWithoutCacheCount += computedWithoutCache ? 1 : 0; - return mapDefined(moduleSpecifiers, (moduleSpecifier): FixAddNewImport | FixAddJsdocTypeImport | undefined => { - if (rejectNodeModulesRelativePaths && pathContainsNodeModules(moduleSpecifier)) { - return undefined; - } - if (!importedSymbolHasValueMeaning && isJs && usagePosition !== undefined) { - // `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types. - return { kind: ImportFixKind.JsdocTypeImport, moduleSpecifier, usagePosition, exportInfo, isReExport: i > 0 }; - } - const importKind = getImportKind(sourceFile, exportInfo.exportKind, compilerOptions); - let qualification: Qualification | undefined; - if (usagePosition !== undefined && importKind === ImportKind.CommonJS && exportInfo.exportKind === ExportKind.Named) { - // Compiler options are restricting our import options to a require, but we need to access - // a named export or property of the exporting module. We need to import the entire module - // and insert a property access, e.g. `writeFile` becomes - // - // import fs = require("fs"); // or const in JS - // fs.writeFile - const exportEquals = checker.resolveExternalModuleSymbol(exportInfo.moduleSymbol); - let namespacePrefix; - if (exportEquals !== exportInfo.moduleSymbol) { - namespacePrefix = getDefaultExportInfoWorker(exportEquals, checker, compilerOptions)?.name; - } - namespacePrefix ||= moduleSymbolToValidIdentifier( - exportInfo.moduleSymbol, - getEmitScriptTarget(compilerOptions), - /*forceCapitalize*/ false); - qualification = { namespacePrefix, usagePosition }; - } - return { - kind: ImportFixKind.AddNew, - moduleSpecifier, - importKind, - useRequire, - addAsTypeOnly, - exportInfo, - isReExport: i > 0, - qualification, - }; - }); - }); - - return { computedWithoutCacheCount, fixes }; -} - -function getFixesForAddImport( - exportInfos: readonly SymbolExportInfo[], - existingImports: readonly FixAddToExistingImportInfo[], - program: Program, - sourceFile: SourceFile, - usagePosition: number | undefined, - isValidTypeOnlyUseSite: boolean, - useRequire: boolean, - host: LanguageServiceHost, - preferences: UserPreferences, - fromCacheOnly?: boolean, -): { computedWithoutCacheCount?: number, fixes: readonly (FixAddNewImport | FixAddJsdocTypeImport)[] } { - const existingDeclaration = firstDefined(existingImports, info => newImportInfoFromExistingSpecifier(info, isValidTypeOnlyUseSite, useRequire, program.getTypeChecker(), program.getCompilerOptions())); - return existingDeclaration ? { fixes: [existingDeclaration] } : getNewImportFixes(program, sourceFile, usagePosition, isValidTypeOnlyUseSite, useRequire, exportInfos, host, preferences, fromCacheOnly); -} - -function newImportInfoFromExistingSpecifier( - { declaration, importKind, symbol, targetFlags }: FixAddToExistingImportInfo, - isValidTypeOnlyUseSite: boolean, - useRequire: boolean, - checker: TypeChecker, - compilerOptions: CompilerOptions -): FixAddNewImport | undefined { - const moduleSpecifier = tryGetModuleSpecifierFromDeclaration(declaration)?.text; - if (moduleSpecifier) { - const addAsTypeOnly = useRequire - ? AddAsTypeOnly.NotAllowed - : getAddAsTypeOnly(isValidTypeOnlyUseSite, /*isForNewImportDeclaration*/ true, symbol, targetFlags, checker, compilerOptions); - return { kind: ImportFixKind.AddNew, moduleSpecifier, importKind, addAsTypeOnly, useRequire }; - } -} - -interface FixInfo { - readonly fix: ImportFix; - readonly symbolName: string; - readonly errorIdentifierText: string | undefined; - readonly isJsxNamespaceFix?: boolean; -} -function getFixInfos(context: CodeFixContextBase, errorCode: number, pos: number, useAutoImportProvider: boolean): readonly FixInfo[] | undefined { - const symbolToken = getTokenAtPosition(context.sourceFile, pos); - let info; - if (errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code) { - info = getFixesInfoForUMDImport(context, symbolToken); - } - else if (!isIdentifier(symbolToken)) { - return undefined; - } - else if (errorCode === Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type.code) { - const symbolName = single(getSymbolNamesToImport(context.sourceFile, context.program.getTypeChecker(), symbolToken, context.program.getCompilerOptions())); - const fix = getTypeOnlyPromotionFix(context.sourceFile, symbolToken, symbolName, context.program); - return fix && [{ fix, symbolName, errorIdentifierText: symbolToken.text }]; - } - else { - info = getFixesInfoForNonUMDImport(context, symbolToken, useAutoImportProvider); - } - - const packageJsonImportFilter = createPackageJsonImportFilter(context.sourceFile, context.preferences, context.host); - return info && sortFixInfo(info, context.sourceFile, context.program, packageJsonImportFilter, context.host); -} - -function sortFixInfo(fixes: readonly (FixInfo & { fix: ImportFixWithModuleSpecifier })[], sourceFile: SourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter, host: LanguageServiceHost): readonly (FixInfo & { fix: ImportFixWithModuleSpecifier })[] { - const _toPath = (fileName: string) => toPath(fileName, host.getCurrentDirectory(), hostGetCanonicalFileName(host)); - return sort(fixes, (a, b) => - compareBooleans(!!a.isJsxNamespaceFix, !!b.isJsxNamespaceFix) || - compareValues(a.fix.kind, b.fix.kind) || - compareModuleSpecifiers(a.fix, b.fix, sourceFile, program, packageJsonImportFilter.allowsImportingSpecifier, _toPath)); -} - -function getBestFix(fixes: readonly ImportFixWithModuleSpecifier[], sourceFile: SourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter, host: LanguageServiceHost): ImportFixWithModuleSpecifier | undefined { - if (!some(fixes)) return; - // These will always be placed first if available, and are better than other kinds - if (fixes[0].kind === ImportFixKind.UseNamespace || fixes[0].kind === ImportFixKind.AddToExisting) { - return fixes[0]; - } - - return fixes.reduce((best, fix) => - // Takes true branch of conditional if `fix` is better than `best` - compareModuleSpecifiers( - fix, - best, - sourceFile, - program, - packageJsonImportFilter.allowsImportingSpecifier, - fileName => toPath(fileName, host.getCurrentDirectory(), hostGetCanonicalFileName(host)), - ) === Comparison.LessThan ? fix : best - ); -} - -/** @returns `Comparison.LessThan` if `a` is better than `b`. */ -function compareModuleSpecifiers( - a: ImportFixWithModuleSpecifier, - b: ImportFixWithModuleSpecifier, - importingFile: SourceFile, - program: Program, - allowsImportingSpecifier: (specifier: string) => boolean, - toPath: (fileName: string) => Path, -): Comparison { - if (a.kind !== ImportFixKind.UseNamespace && b.kind !== ImportFixKind.UseNamespace) { - return compareBooleans(allowsImportingSpecifier(b.moduleSpecifier), allowsImportingSpecifier(a.moduleSpecifier)) - || compareNodeCoreModuleSpecifiers(a.moduleSpecifier, b.moduleSpecifier, importingFile, program) - || compareBooleans( - isFixPossiblyReExportingImportingFile(a, importingFile, program.getCompilerOptions(), toPath), - isFixPossiblyReExportingImportingFile(b, importingFile, program.getCompilerOptions(), toPath)) - || compareNumberOfDirectorySeparators(a.moduleSpecifier, b.moduleSpecifier); - } - return Comparison.EqualTo; -} - -// This is a simple heuristic to try to avoid creating an import cycle with a barrel re-export. -// E.g., do not `import { Foo } from ".."` when you could `import { Foo } from "../Foo"`. -// This can produce false positives or negatives if re-exports cross into sibling directories -// (e.g. `export * from "../whatever"`) or are not named "index" (we don't even try to consider -// this if we're in a resolution mode where you can't drop trailing "/index" from paths). -function isFixPossiblyReExportingImportingFile(fix: ImportFixWithModuleSpecifier, importingFile: SourceFile, compilerOptions: CompilerOptions, toPath: (fileName: string) => Path): boolean { - if (fix.isReExport && - fix.exportInfo?.moduleFileName && - getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node10 && - isIndexFileName(fix.exportInfo.moduleFileName) - ) { - const reExportDir = toPath(getDirectoryPath(fix.exportInfo.moduleFileName)); - return startsWith((importingFile.path), reExportDir); - } - return false; -} - -function isIndexFileName(fileName: string) { - return getBaseFileName(fileName, [".js", ".jsx", ".d.ts", ".ts", ".tsx"], /*ignoreCase*/ true) === "index"; -} - -function compareNodeCoreModuleSpecifiers(a: string, b: string, importingFile: SourceFile, program: Program): Comparison { - if (startsWith(a, "node:") && !startsWith(b, "node:")) return shouldUseUriStyleNodeCoreModules(importingFile, program) ? Comparison.LessThan : Comparison.GreaterThan; - if (startsWith(b, "node:") && !startsWith(a, "node:")) return shouldUseUriStyleNodeCoreModules(importingFile, program) ? Comparison.GreaterThan : Comparison.LessThan; - return Comparison.EqualTo; -} - -function getFixesInfoForUMDImport({ sourceFile, program, host, preferences }: CodeFixContextBase, token: Node): (FixInfo & { fix: ImportFixWithModuleSpecifier })[] | undefined { - const checker = program.getTypeChecker(); - const umdSymbol = getUmdSymbol(token, checker); - if (!umdSymbol) return undefined; - const symbol = checker.getAliasedSymbol(umdSymbol); - const symbolName = umdSymbol.name; - const exportInfo: readonly SymbolExportInfo[] = [{ symbol: umdSymbol, moduleSymbol: symbol, moduleFileName: undefined, exportKind: ExportKind.UMD, targetFlags: symbol.flags, isFromPackageJson: false }]; - const useRequire = shouldUseRequire(sourceFile, program); - // `usagePosition` is undefined because `token` may not actually be a usage of the symbol we're importing. - // For example, we might need to import `React` in order to use an arbitrary JSX tag. We could send a position - // for other UMD imports, but `usagePosition` is currently only used to insert a namespace qualification - // before a named import, like converting `writeFile` to `fs.writeFile` (whether `fs` is already imported or - // not), and this function will only be called for UMD symbols, which are necessarily an `export =`, not a - // named export. - const fixes = getImportFixes(exportInfo, /*usagePosition*/ undefined, /*isValidTypeOnlyUseSite*/ false, useRequire, program, sourceFile, host, preferences).fixes; - return fixes.map(fix => ({ fix, symbolName, errorIdentifierText: tryCast(token, isIdentifier)?.text })); -} -function getUmdSymbol(token: Node, checker: TypeChecker): Symbol | undefined { - // try the identifier to see if it is the umd symbol - const umdSymbol = isIdentifier(token) ? checker.getSymbolAtLocation(token) : undefined; - if (isUMDExportSymbol(umdSymbol)) return umdSymbol; - - // The error wasn't for the symbolAtLocation, it was for the JSX tag itself, which needs access to e.g. `React`. - const { parent } = token; - if ((isJsxOpeningLikeElement(parent) && parent.tagName === token) || isJsxOpeningFragment(parent)) { - const parentSymbol = checker.resolveName(checker.getJsxNamespace(parent), isJsxOpeningLikeElement(parent) ? token : parent, SymbolFlags.Value, /*excludeGlobals*/ false); - if (isUMDExportSymbol(parentSymbol)) { - return parentSymbol; - } - } - return undefined; -} - -/** - * @param forceImportKeyword Indicates that the user has already typed `import`, so the result must start with `import`. - * (In other words, do not allow `const x = require("...")` for JS files.) - * - * @internal - */ -export function getImportKind(importingFile: SourceFile, exportKind: ExportKind, compilerOptions: CompilerOptions, forceImportKeyword?: boolean): ImportKind { - if (compilerOptions.verbatimModuleSyntax && (getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS || importingFile.impliedNodeFormat === ModuleKind.CommonJS)) { - // TODO: if the exporting file is ESM under nodenext, or `forceImport` is given in a JS file, this is impossible - return ImportKind.CommonJS; - } - switch (exportKind) { - case ExportKind.Named: return ImportKind.Named; - case ExportKind.Default: return ImportKind.Default; - case ExportKind.ExportEquals: return getExportEqualsImportKind(importingFile, compilerOptions, !!forceImportKeyword); - case ExportKind.UMD: return getUmdImportKind(importingFile, compilerOptions, !!forceImportKeyword); - default: return Debug.assertNever(exportKind); - } -} - -function getUmdImportKind(importingFile: SourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind { - // Import a synthetic `default` if enabled. - if (getAllowSyntheticDefaultImports(compilerOptions)) { - return ImportKind.Default; - } - - // When a synthetic `default` is unavailable, use `import..require` if the module kind supports it. - const moduleKind = getEmitModuleKind(compilerOptions); - switch (moduleKind) { - case ModuleKind.AMD: - case ModuleKind.CommonJS: - case ModuleKind.UMD: - if (isInJSFile(importingFile)) { - return isExternalModule(importingFile) || forceImportKeyword ? ImportKind.Namespace : ImportKind.CommonJS; - } - return ImportKind.CommonJS; - case ModuleKind.System: - case ModuleKind.ES2015: - case ModuleKind.ES2020: - case ModuleKind.ES2022: - case ModuleKind.ESNext: - case ModuleKind.None: - // Fall back to the `import * as ns` style import. - return ImportKind.Namespace; - case ModuleKind.Node16: - case ModuleKind.NodeNext: - return importingFile.impliedNodeFormat === ModuleKind.ESNext ? ImportKind.Namespace : ImportKind.CommonJS; - default: - return Debug.assertNever(moduleKind, `Unexpected moduleKind ${moduleKind}`); - } -} - -function getFixesInfoForNonUMDImport({ sourceFile, program, cancellationToken, host, preferences }: CodeFixContextBase, symbolToken: Identifier, useAutoImportProvider: boolean): readonly (FixInfo & { fix: ImportFixWithModuleSpecifier })[] | undefined { - const checker = program.getTypeChecker(); - const compilerOptions = program.getCompilerOptions(); - return flatMap(getSymbolNamesToImport(sourceFile, checker, symbolToken, compilerOptions), symbolName => { - // "default" is a keyword and not a legal identifier for the import, but appears as an identifier. - if (symbolName === InternalSymbolName.Default) { - return undefined; - } - const isValidTypeOnlyUseSite = isValidTypeOnlyAliasUseSite(symbolToken); - const useRequire = shouldUseRequire(sourceFile, program); - const exportInfo = getExportInfos(symbolName, isJSXTagName(symbolToken), getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, program, useAutoImportProvider, host, preferences); - return arrayFrom( - flatMapIterator(exportInfo.values(), exportInfos => - getImportFixes(exportInfos, symbolToken.getStart(sourceFile), isValidTypeOnlyUseSite, useRequire, program, sourceFile, host, preferences).fixes), - fix => ({ fix, symbolName, errorIdentifierText: symbolToken.text, isJsxNamespaceFix: symbolName !== symbolToken.text }) - ); - }); -} - -function getTypeOnlyPromotionFix(sourceFile: SourceFile, symbolToken: Identifier, symbolName: string, program: Program): FixPromoteTypeOnlyImport | undefined { - const checker = program.getTypeChecker(); - const symbol = checker.resolveName(symbolName, symbolToken, SymbolFlags.Value, /*excludeGlobals*/ true); - if (!symbol) return undefined; - - const typeOnlyAliasDeclaration = checker.getTypeOnlyAliasDeclaration(symbol); - if (!typeOnlyAliasDeclaration || getSourceFileOfNode(typeOnlyAliasDeclaration) !== sourceFile) return undefined; - - return { kind: ImportFixKind.PromoteTypeOnly, typeOnlyAliasDeclaration }; -} - -function getSymbolNamesToImport(sourceFile: SourceFile, checker: TypeChecker, symbolToken: Identifier, compilerOptions: CompilerOptions): string[] { - const parent = symbolToken.parent; - if ((isJsxOpeningLikeElement(parent) || isJsxClosingElement(parent)) && parent.tagName === symbolToken && jsxModeNeedsExplicitImport(compilerOptions.jsx)) { - const jsxNamespace = checker.getJsxNamespace(sourceFile); - if (needsJsxNamespaceFix(jsxNamespace, symbolToken, checker)) { - const needsComponentNameFix = !isIntrinsicJsxName(symbolToken.text) && !checker.resolveName(symbolToken.text, symbolToken, SymbolFlags.Value, /*excludeGlobals*/ false); - return needsComponentNameFix ? [symbolToken.text, jsxNamespace] : [jsxNamespace]; - } - } - return [symbolToken.text]; -} - -function needsJsxNamespaceFix(jsxNamespace: string, symbolToken: Identifier, checker: TypeChecker) { - if (isIntrinsicJsxName(symbolToken.text)) return true; // If we were triggered by a matching error code on an intrinsic, the error must have been about missing the JSX factory - const namespaceSymbol = checker.resolveName(jsxNamespace, symbolToken, SymbolFlags.Value, /*excludeGlobals*/ true); - return !namespaceSymbol || some(namespaceSymbol.declarations, isTypeOnlyImportOrExportDeclaration) && !(namespaceSymbol.flags & SymbolFlags.Value); -} - -// Returns a map from an exported symbol's ID to a list of every way it's (re-)exported. -function getExportInfos( - symbolName: string, - isJsxTagName: boolean, - currentTokenMeaning: SemanticMeaning, - cancellationToken: CancellationToken, - fromFile: SourceFile, - program: Program, - useAutoImportProvider: boolean, - host: LanguageServiceHost, - preferences: UserPreferences, -): ReadonlyMap { - // For each original symbol, keep all re-exports of that symbol together so we can call `getCodeActionsForImport` on the whole group at once. - // Maps symbol id to info for modules providing that symbol (original export + re-exports). - const originalSymbolToExportInfos = createMultiMap(); - const packageJsonFilter = createPackageJsonImportFilter(fromFile, preferences, host); - const moduleSpecifierCache = host.getModuleSpecifierCache?.(); - const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => { - return createModuleSpecifierResolutionHost(isFromPackageJson ? host.getPackageJsonAutoImportProvider!()! : program, host); - }); - function addSymbol(moduleSymbol: Symbol, toFile: SourceFile | undefined, exportedSymbol: Symbol, exportKind: ExportKind, program: Program, isFromPackageJson: boolean): void { - const moduleSpecifierResolutionHost = getModuleSpecifierResolutionHost(isFromPackageJson); - if (toFile && isImportableFile(program, fromFile, toFile, preferences, packageJsonFilter, moduleSpecifierResolutionHost, moduleSpecifierCache) || - !toFile && packageJsonFilter.allowsImportingAmbientModule(moduleSymbol, moduleSpecifierResolutionHost) - ) { - const checker = program.getTypeChecker(); - originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { symbol: exportedSymbol, moduleSymbol, moduleFileName: toFile?.fileName, exportKind, targetFlags: skipAlias(exportedSymbol, checker).flags, isFromPackageJson }); - } - } - forEachExternalModuleToImportFrom(program, host, preferences, useAutoImportProvider, (moduleSymbol, sourceFile, program, isFromPackageJson) => { - const checker = program.getTypeChecker(); - cancellationToken.throwIfCancellationRequested(); - - const compilerOptions = program.getCompilerOptions(); - const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions); - if (defaultInfo && (defaultInfo.name === symbolName || moduleSymbolToValidIdentifier(moduleSymbol, getEmitScriptTarget(compilerOptions), isJsxTagName) === symbolName) && symbolHasMeaning(defaultInfo.resolvedSymbol, currentTokenMeaning)) { - addSymbol(moduleSymbol, sourceFile, defaultInfo.symbol, defaultInfo.exportKind, program, isFromPackageJson); - } - - // check exports with the same name - const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExportsAndProperties(symbolName, moduleSymbol); - if (exportSymbolWithIdenticalName && symbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) { - addSymbol(moduleSymbol, sourceFile, exportSymbolWithIdenticalName, ExportKind.Named, program, isFromPackageJson); - } - }); - return originalSymbolToExportInfos; -} - -function getExportEqualsImportKind(importingFile: SourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind { - const allowSyntheticDefaults = getAllowSyntheticDefaultImports(compilerOptions); - const isJS = isInJSFile(importingFile); - // 1. 'import =' will not work in es2015+ TS files, so the decision is between a default - // and a namespace import, based on allowSyntheticDefaultImports/esModuleInterop. - if (!isJS && getEmitModuleKind(compilerOptions) >= ModuleKind.ES2015) { - return allowSyntheticDefaults ? ImportKind.Default : ImportKind.Namespace; - } - // 2. 'import =' will not work in JavaScript, so the decision is between a default import, - // a namespace import, and const/require. - if (isJS) { - return isExternalModule(importingFile) || forceImportKeyword - ? allowSyntheticDefaults ? ImportKind.Default : ImportKind.Namespace - : ImportKind.CommonJS; - } - // 3. At this point the most correct choice is probably 'import =', but people - // really hate that, so look to see if the importing file has any precedent - // on how to handle it. - for (const statement of importingFile.statements) { - // `import foo` parses as an ImportEqualsDeclaration even though it could be an ImportDeclaration - if (isImportEqualsDeclaration(statement) && !nodeIsMissing(statement.moduleReference)) { - return ImportKind.CommonJS; - } - } - // 4. We have no precedent to go on, so just use a default import if - // allowSyntheticDefaultImports/esModuleInterop is enabled. - return allowSyntheticDefaults ? ImportKind.Default : ImportKind.CommonJS; -} - -function codeActionForFix(context: textChanges.TextChangesContext, sourceFile: SourceFile, symbolName: string, fix: ImportFix, includeSymbolNameInDescription: boolean, compilerOptions: CompilerOptions, preferences: UserPreferences): CodeFixAction { - let diag!: DiagnosticOrDiagnosticAndArguments; - const changes = textChanges.ChangeTracker.with(context, tracker => { - diag = codeActionForFixWorker(tracker, sourceFile, symbolName, fix, includeSymbolNameInDescription, compilerOptions, preferences); - }); - return createCodeFixAction(importFixName, changes, diag, importFixId, Diagnostics.Add_all_missing_imports); -} -function codeActionForFixWorker(changes: textChanges.ChangeTracker, sourceFile: SourceFile, symbolName: string, fix: ImportFix, includeSymbolNameInDescription: boolean, compilerOptions: CompilerOptions, preferences: UserPreferences): DiagnosticOrDiagnosticAndArguments { - const quotePreference = getQuotePreference(sourceFile, preferences); - switch (fix.kind) { - case ImportFixKind.UseNamespace: - addNamespaceQualifier(changes, sourceFile, fix); - return [Diagnostics.Change_0_to_1, symbolName, `${fix.namespacePrefix}.${symbolName}`]; - case ImportFixKind.JsdocTypeImport: - addImportType(changes, sourceFile, fix, quotePreference); - return [Diagnostics.Change_0_to_1, symbolName, getImportTypePrefix(fix.moduleSpecifier, quotePreference) + symbolName]; - case ImportFixKind.AddToExisting: { - const { importClauseOrBindingPattern, importKind, addAsTypeOnly, moduleSpecifier } = fix; - doAddExistingFix( - changes, - sourceFile, - importClauseOrBindingPattern, - importKind === ImportKind.Default ? { name: symbolName, addAsTypeOnly } : undefined, - importKind === ImportKind.Named ? [{ name: symbolName, addAsTypeOnly }] : emptyArray, - preferences); - const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier); - return includeSymbolNameInDescription - ? [Diagnostics.Import_0_from_1, symbolName, moduleSpecifierWithoutQuotes] - : [Diagnostics.Update_import_from_0, moduleSpecifierWithoutQuotes]; - } - case ImportFixKind.AddNew: { - const { importKind, moduleSpecifier, addAsTypeOnly, useRequire, qualification } = fix; - const getDeclarations = useRequire ? getNewRequires : getNewImports; - const defaultImport: Import | undefined = importKind === ImportKind.Default ? { name: symbolName, addAsTypeOnly } : undefined; - const namedImports: Import[] | undefined = importKind === ImportKind.Named ? [{ name: symbolName, addAsTypeOnly }] : undefined; - const namespaceLikeImport = importKind === ImportKind.Namespace || importKind === ImportKind.CommonJS - ? { importKind, name: qualification?.namespacePrefix || symbolName, addAsTypeOnly } - : undefined; - insertImports(changes, sourceFile, getDeclarations( - moduleSpecifier, - quotePreference, - defaultImport, - namedImports, - namespaceLikeImport, - compilerOptions), /*blankLineBetween*/ true, preferences); - if (qualification) { - addNamespaceQualifier(changes, sourceFile, qualification); - } - return includeSymbolNameInDescription - ? [Diagnostics.Import_0_from_1, symbolName, moduleSpecifier] - : [Diagnostics.Add_import_from_0, moduleSpecifier]; - } - case ImportFixKind.PromoteTypeOnly: { - const { typeOnlyAliasDeclaration } = fix; - const promotedDeclaration = promoteFromTypeOnly(changes, typeOnlyAliasDeclaration, compilerOptions, sourceFile, preferences); - return promotedDeclaration.kind === SyntaxKind.ImportSpecifier - ? [Diagnostics.Remove_type_from_import_of_0_from_1, symbolName, getModuleSpecifierText(promotedDeclaration.parent.parent)] - : [Diagnostics.Remove_type_from_import_declaration_from_0, getModuleSpecifierText(promotedDeclaration)]; - } - default: - return Debug.assertNever(fix, `Unexpected fix kind ${(fix as ImportFix).kind}`); - } -} - -function getModuleSpecifierText(promotedDeclaration: ImportClause | ImportEqualsDeclaration): string { - return promotedDeclaration.kind === SyntaxKind.ImportEqualsDeclaration - ? tryCast(tryCast(promotedDeclaration.moduleReference, isExternalModuleReference)?.expression, isStringLiteralLike)?.text || promotedDeclaration.moduleReference.getText() - : cast(promotedDeclaration.parent.moduleSpecifier, isStringLiteral).text; -} - -function promoteFromTypeOnly(changes: textChanges.ChangeTracker, aliasDeclaration: TypeOnlyAliasDeclaration, compilerOptions: CompilerOptions, sourceFile: SourceFile, preferences: UserPreferences) { - // See comment in `doAddExistingFix` on constant with the same name. - const convertExistingToTypeOnly = importNameElisionDisabled(compilerOptions); - switch (aliasDeclaration.kind) { - case SyntaxKind.ImportSpecifier: - if (aliasDeclaration.isTypeOnly) { - const sortKind = OrganizeImports.detectImportSpecifierSorting(aliasDeclaration.parent.elements, preferences); - if (aliasDeclaration.parent.elements.length > 1 && sortKind) { - changes.delete(sourceFile, aliasDeclaration); - const newSpecifier = factory.updateImportSpecifier(aliasDeclaration, /*isTypeOnly*/ false, aliasDeclaration.propertyName, aliasDeclaration.name); - const comparer = OrganizeImports.getOrganizeImportsComparer(preferences, sortKind === SortKind.CaseInsensitive); - const insertionIndex = OrganizeImports.getImportSpecifierInsertionIndex(aliasDeclaration.parent.elements, newSpecifier, comparer); - changes.insertImportSpecifierAtIndex(sourceFile, newSpecifier, aliasDeclaration.parent, insertionIndex); - } - else { - changes.deleteRange(sourceFile, aliasDeclaration.getFirstToken()!); - } - return aliasDeclaration; - } - else { - Debug.assert(aliasDeclaration.parent.parent.isTypeOnly); - promoteImportClause(aliasDeclaration.parent.parent); - return aliasDeclaration.parent.parent; - } - case SyntaxKind.ImportClause: - promoteImportClause(aliasDeclaration); - return aliasDeclaration; - case SyntaxKind.NamespaceImport: - promoteImportClause(aliasDeclaration.parent); - return aliasDeclaration.parent; - case SyntaxKind.ImportEqualsDeclaration: - changes.deleteRange(sourceFile, aliasDeclaration.getChildAt(1)); - return aliasDeclaration; - default: - Debug.failBadSyntaxKind(aliasDeclaration); - } - - function promoteImportClause(importClause: ImportClause) { - changes.delete(sourceFile, getTypeKeywordOfTypeOnlyImport(importClause, sourceFile)); - if (convertExistingToTypeOnly) { - const namedImports = tryCast(importClause.namedBindings, isNamedImports); - if (namedImports && namedImports.elements.length > 1) { - if (OrganizeImports.detectImportSpecifierSorting(namedImports.elements, preferences) && - aliasDeclaration.kind === SyntaxKind.ImportSpecifier && - namedImports.elements.indexOf(aliasDeclaration) !== 0 - ) { - // The import specifier being promoted will be the only non-type-only, - // import in the NamedImports, so it should be moved to the front. - changes.delete(sourceFile, aliasDeclaration); - changes.insertImportSpecifierAtIndex(sourceFile, aliasDeclaration, namedImports, 0); - } - for (const element of namedImports.elements) { - if (element !== aliasDeclaration && !element.isTypeOnly) { - changes.insertModifierBefore(sourceFile, SyntaxKind.TypeKeyword, element); - } - } - } - } - } -} - -function doAddExistingFix( - changes: textChanges.ChangeTracker, - sourceFile: SourceFile, - clause: ImportClause | ObjectBindingPattern, - defaultImport: Import | undefined, - namedImports: readonly Import[], - preferences: UserPreferences, -): void { - if (clause.kind === SyntaxKind.ObjectBindingPattern) { - if (defaultImport) { - addElementToBindingPattern(clause, defaultImport.name, "default"); - } - for (const specifier of namedImports) { - addElementToBindingPattern(clause, specifier.name, /*propertyName*/ undefined); - } - return; - } - - const promoteFromTypeOnly = clause.isTypeOnly && some([defaultImport, ...namedImports], i => i?.addAsTypeOnly === AddAsTypeOnly.NotAllowed); - const existingSpecifiers = clause.namedBindings && tryCast(clause.namedBindings, isNamedImports)?.elements; - - if (defaultImport) { - Debug.assert(!clause.name, "Cannot add a default import to an import clause that already has one"); - changes.insertNodeAt(sourceFile, clause.getStart(sourceFile), factory.createIdentifier(defaultImport.name), { suffix: ", " }); - } - - if (namedImports.length) { - // sort case sensitivity: - // - if the user preference is explicit, use that - // - otherwise, if there are enough existing import specifiers in this import to detect unambiguously, use that - // - otherwise, detect from other imports in the file - let ignoreCaseForSorting: boolean | undefined; - if (typeof preferences.organizeImportsIgnoreCase === "boolean") { - ignoreCaseForSorting = preferences.organizeImportsIgnoreCase; - } - else if (existingSpecifiers) { - const targetImportSorting = OrganizeImports.detectImportSpecifierSorting(existingSpecifiers, preferences); - if (targetImportSorting !== SortKind.Both) { - ignoreCaseForSorting = targetImportSorting === SortKind.CaseInsensitive; - } - } - if (ignoreCaseForSorting === undefined) { - ignoreCaseForSorting = OrganizeImports.detectSorting(sourceFile, preferences) === SortKind.CaseInsensitive; - } - - const comparer = OrganizeImports.getOrganizeImportsComparer(preferences, ignoreCaseForSorting); - const newSpecifiers = stableSort( - namedImports.map(namedImport => factory.createImportSpecifier( - (!clause.isTypeOnly || promoteFromTypeOnly) && needsTypeOnly(namedImport), - /*propertyName*/ undefined, - factory.createIdentifier(namedImport.name))), - (s1, s2) => OrganizeImports.compareImportOrExportSpecifiers(s1, s2, comparer)); - - // The sorting preference computed earlier may or may not have validated that these particular - // import specifiers are sorted. If they aren't, `getImportSpecifierInsertionIndex` will return - // nonsense. So if there are existing specifiers, even if we know the sorting preference, we - // need to ensure that the existing specifiers are sorted according to the preference in order - // to do a sorted insertion. - const specifierSort = existingSpecifiers?.length && OrganizeImports.detectImportSpecifierSorting(existingSpecifiers, preferences); - if (specifierSort && !(ignoreCaseForSorting && specifierSort === SortKind.CaseSensitive)) { - for (const spec of newSpecifiers) { - // Organize imports puts type-only import specifiers last, so if we're - // adding a non-type-only specifier and converting all the other ones to - // type-only, there's no need to ask for the insertion index - it's 0. - const insertionIndex = promoteFromTypeOnly && !spec.isTypeOnly - ? 0 - : OrganizeImports.getImportSpecifierInsertionIndex(existingSpecifiers, spec, comparer); - changes.insertImportSpecifierAtIndex(sourceFile, spec, clause.namedBindings as NamedImports, insertionIndex); - } - } - else if (existingSpecifiers?.length) { - for (const spec of newSpecifiers) { - changes.insertNodeInListAfter(sourceFile, last(existingSpecifiers), spec, existingSpecifiers); - } - } - else { - if (newSpecifiers.length) { - const namedImports = factory.createNamedImports(newSpecifiers); - if (clause.namedBindings) { - changes.replaceNode(sourceFile, clause.namedBindings, namedImports); - } - else { - changes.insertNodeAfter(sourceFile, Debug.checkDefined(clause.name, "Import clause must have either named imports or a default import"), namedImports); - } - } - } - } - - if (promoteFromTypeOnly) { - changes.delete(sourceFile, getTypeKeywordOfTypeOnlyImport(clause, sourceFile)); - if (existingSpecifiers) { - // We used to convert existing specifiers to type-only only if compiler options indicated that - // would be meaningful (see the `importNameElisionDisabled` utility function), but user - // feedback indicated a preference for preserving the type-onlyness of existing specifiers - // regardless of whether it would make a difference in emit. - for (const specifier of existingSpecifiers) { - changes.insertModifierBefore(sourceFile, SyntaxKind.TypeKeyword, specifier); - } - } - } - - function addElementToBindingPattern(bindingPattern: ObjectBindingPattern, name: string, propertyName: string | undefined) { - const element = factory.createBindingElement(/*dotDotDotToken*/ undefined, propertyName, name); - if (bindingPattern.elements.length) { - changes.insertNodeInListAfter(sourceFile, last(bindingPattern.elements), element); - } - else { - changes.replaceNode(sourceFile, bindingPattern, factory.createObjectBindingPattern([element])); - } - } -} - -function addNamespaceQualifier(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { namespacePrefix, usagePosition }: Qualification): void { - changes.insertText(sourceFile, usagePosition, namespacePrefix + "."); -} - -function addImportType(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { moduleSpecifier, usagePosition: position }: FixAddJsdocTypeImport, quotePreference: QuotePreference): void { - changes.insertText(sourceFile, position, getImportTypePrefix(moduleSpecifier, quotePreference)); -} - -function getImportTypePrefix(moduleSpecifier: string, quotePreference: QuotePreference): string { - const quote = getQuoteFromPreference(quotePreference); - return `import(${quote}${moduleSpecifier}${quote}).`; -} - -interface Import { - readonly name: string; - readonly addAsTypeOnly: AddAsTypeOnly; -} - -interface ImportsCollection { - readonly defaultImport?: Import; - readonly namedImports?: Map; - readonly namespaceLikeImport?: { - readonly importKind: ImportKind.CommonJS | ImportKind.Namespace; - readonly name: string; - readonly addAsTypeOnly: AddAsTypeOnly; - }; -} - -function needsTypeOnly({ addAsTypeOnly }: { addAsTypeOnly: AddAsTypeOnly }): boolean { - return addAsTypeOnly === AddAsTypeOnly.Required; -} - -function getNewImports( - moduleSpecifier: string, - quotePreference: QuotePreference, - defaultImport: Import | undefined, - namedImports: readonly Import[] | undefined, - namespaceLikeImport: Import & { importKind: ImportKind.CommonJS | ImportKind.Namespace } | undefined, - compilerOptions: CompilerOptions, -): AnyImportSyntax | readonly AnyImportSyntax[] { - const quotedModuleSpecifier = makeStringLiteral(moduleSpecifier, quotePreference); - let statements: AnyImportSyntax | readonly AnyImportSyntax[] | undefined; - if (defaultImport !== undefined || namedImports?.length) { - // `verbatimModuleSyntax` should prefer top-level `import type` - - // even though it's not an error, it would add unnecessary runtime emit. - const topLevelTypeOnly = (!defaultImport || needsTypeOnly(defaultImport)) && every(namedImports, needsTypeOnly) || - compilerOptions.verbatimModuleSyntax && - defaultImport?.addAsTypeOnly !== AddAsTypeOnly.NotAllowed && - !some(namedImports, i => i.addAsTypeOnly === AddAsTypeOnly.NotAllowed); - statements = combine(statements, makeImport( - defaultImport && factory.createIdentifier(defaultImport.name), - namedImports?.map(({ addAsTypeOnly, name }) => factory.createImportSpecifier( - !topLevelTypeOnly && addAsTypeOnly === AddAsTypeOnly.Required, - /*propertyName*/ undefined, - factory.createIdentifier(name))), - moduleSpecifier, - quotePreference, - topLevelTypeOnly)); - } - - if (namespaceLikeImport) { - const declaration = namespaceLikeImport.importKind === ImportKind.CommonJS - ? factory.createImportEqualsDeclaration( - /*modifiers*/ undefined, - needsTypeOnly(namespaceLikeImport), - factory.createIdentifier(namespaceLikeImport.name), - factory.createExternalModuleReference(quotedModuleSpecifier)) - : factory.createImportDeclaration( - /*modifiers*/ undefined, - factory.createImportClause( - needsTypeOnly(namespaceLikeImport), - /*name*/ undefined, - factory.createNamespaceImport(factory.createIdentifier(namespaceLikeImport.name))), - quotedModuleSpecifier, - /*assertClause*/ undefined); - statements = combine(statements, declaration); - } - return Debug.checkDefined(statements); -} - -function getNewRequires(moduleSpecifier: string, quotePreference: QuotePreference, defaultImport: Import | undefined, namedImports: readonly Import[] | undefined, namespaceLikeImport: Import | undefined): RequireVariableStatement | readonly RequireVariableStatement[] { - const quotedModuleSpecifier = makeStringLiteral(moduleSpecifier, quotePreference); - let statements: RequireVariableStatement | readonly RequireVariableStatement[] | undefined; - // const { default: foo, bar, etc } = require('./mod'); - if (defaultImport || namedImports?.length) { - const bindingElements = namedImports?.map(({ name }) => factory.createBindingElement(/*dotDotDotToken*/ undefined, /*propertyName*/ undefined, name)) || []; - if (defaultImport) { - bindingElements.unshift(factory.createBindingElement(/*dotDotDotToken*/ undefined, "default", defaultImport.name)); - } - const declaration = createConstEqualsRequireDeclaration(factory.createObjectBindingPattern(bindingElements), quotedModuleSpecifier); - statements = combine(statements, declaration); - } - // const foo = require('./mod'); - if (namespaceLikeImport) { - const declaration = createConstEqualsRequireDeclaration(namespaceLikeImport.name, quotedModuleSpecifier); - statements = combine(statements, declaration); - } - return Debug.checkDefined(statements); -} - -function createConstEqualsRequireDeclaration(name: string | ObjectBindingPattern, quotedModuleSpecifier: StringLiteral): RequireVariableStatement { - return factory.createVariableStatement( - /*modifiers*/ undefined, - factory.createVariableDeclarationList([ - factory.createVariableDeclaration( - typeof name === "string" ? factory.createIdentifier(name) : name, - /*exclamationToken*/ undefined, - /*type*/ undefined, - factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, [quotedModuleSpecifier]))], - NodeFlags.Const)) as RequireVariableStatement; -} - -function symbolHasMeaning({ declarations }: Symbol, meaning: SemanticMeaning): boolean { - return some(declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning)); -} - -/** @internal */ -export function moduleSymbolToValidIdentifier(moduleSymbol: Symbol, target: ScriptTarget | undefined, forceCapitalize: boolean): string { - return moduleSpecifierToValidIdentifier(removeFileExtension(stripQuotes(moduleSymbol.name)), target, forceCapitalize); -} - -/** @internal */ -export function moduleSpecifierToValidIdentifier(moduleSpecifier: string, target: ScriptTarget | undefined, forceCapitalize?: boolean): string { - const baseName = getBaseFileName(removeSuffix(moduleSpecifier, "/index")); - let res = ""; - let lastCharWasValid = true; - const firstCharCode = baseName.charCodeAt(0); - if (isIdentifierStart(firstCharCode, target)) { - res += String.fromCharCode(firstCharCode); - if (forceCapitalize) { - res = res.toUpperCase(); - } - } - else { - lastCharWasValid = false; - } - for (let i = 1; i < baseName.length; i++) { - const ch = baseName.charCodeAt(i); - const isValid = isIdentifierPart(ch, target); - if (isValid) { - let char = String.fromCharCode(ch); - if (!lastCharWasValid) { - char = char.toUpperCase(); - } - res += char; - } - lastCharWasValid = isValid; - } - // Need `|| "_"` to ensure result isn't empty. - return !isStringANonContextualKeyword(res) ? res || "_" : `_${res}`; -} diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index af21562a22a27..6f74b50cae1bb 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -1,71 +1,55 @@ import { - __String, - AnonymousType, - BinaryExpression, - CallExpression, - CancellationToken, - CaseOrDefaultClause, cast, createMultiMap, - createSymbolTable, - Debug, - Declaration, - DiagnosticMessage, - Diagnostics, - ElementAccessExpression, - EmitFlags, emptyArray, - escapeLeadingUnderscores, - Expression, - factory, - FindAllReferences, - findChildOfKind, first, firstOrUndefined, flatMap, forEach, - forEachEntry, - getContainingFunction, - getEmitScriptTarget, - getJSDocType, - getNameOfDeclaration, - getObjectFlags, - getSourceFileOfNode, - getTextOfNode, - getTokenAtPosition, - getTypeNodeIfAccessible, - Identifier, - IndexKind, + last, + length, + map, + mapDefined, + mapEntries, + returnTrue, + singleOrUndefined, + tryCast, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { setEmitFlags } from "../../compiler/factory/emitNode"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, - isAssignmentExpression, isCallExpression, - isExpressionNode, isExpressionStatement, isFunctionExpression, isGetAccessorDeclaration, isIdentifier, - isInJSFile, isParameter, - isParameterPropertyModifier, isPropertyAccessExpression, isPropertyDeclaration, isPropertySignature, - isRestParameter, - isRightSideOfQualifiedNameOrPropertyAccess, isSetAccessorDeclaration, - isTransientSymbol, isVariableDeclaration, isVariableStatement, - LanguageServiceHost, - last, - length, - map, - mapDefined, - mapEntries, +} from "../../compiler/factory/nodeTests"; +import { + __String, + AnonymousType, + BinaryExpression, + CallExpression, + CancellationToken, + CaseOrDefaultClause, + Declaration, + DiagnosticMessage, + ElementAccessExpression, + EmitFlags, + Expression, + Identifier, + IndexKind, NewExpression, Node, - NodeSeenTracker, - nodeSeenTracker, ObjectFlags, ParameterDeclaration, PrefixUnaryExpression, @@ -76,23 +60,18 @@ import { PropertyDeclaration, PropertyName, PropertySignature, - returnTrue, ScriptTarget, SetAccessorDeclaration, - setEmitFlags, ShorthandPropertyAssignment, Signature, SignatureDeclaration, SignatureFlags, SignatureKind, - singleOrUndefined, SourceFile, Symbol, SymbolFlags, SyntaxKind, - textChanges, Token, - tryCast, Type, TypeFlags, TypeNode, @@ -101,15 +80,56 @@ import { UnionReduction, UserPreferences, VariableDeclaration, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + createSymbolTable, + forEachEntry, + getContainingFunction, + getEmitScriptTarget, + getObjectFlags, + getSourceFileOfNode, + getTextOfNode, + isAssignmentExpression, + isExpressionNode, + isInJSFile, + isRightSideOfQualifiedNameOrPropertyAccess, + isTransientSymbol, +} from "../../compiler/utilities"; +import { + escapeLeadingUnderscores, + getJSDocType, + getNameOfDeclaration, + isParameterPropertyModifier, + isRestParameter, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, + registerCodeFix, +} from "../codeFixProvider"; +import { getReferenceEntriesForNode } from "../findAllReferences"; +import { + ChangeTracker, + isThisTypeAnnotatable, + ThisTypeAnnotatable, + TypeAnnotatable, +} from "../textChanges"; +import { + EntryKind, + LanguageServiceHost, +} from "../types"; +import { + findChildOfKind, + getTokenAtPosition, + getTypeNodeIfAccessible, + NodeSeenTracker, + nodeSeenTracker, +} from "../utilities"; +import { tryGetAutoImportableReferenceFromTypeNode } from "./helpers"; +import { createImportAdder, ImportAdder, - registerCodeFix, - tryGetAutoImportableReferenceFromTypeNode, -} from "../_namespaces/ts.codefix"; +} from "./importAdder"; const fixId = "inferFromUsage"; const errorCodes = [ @@ -164,7 +184,7 @@ registerCodeFix({ const token = getTokenAtPosition(sourceFile, start); let declaration: Declaration | undefined; - const changes = textChanges.ChangeTracker.with(context, changes => { + const changes = ChangeTracker.with(context, changes => { declaration = doChange(changes, sourceFile, token, errorCode, program, cancellationToken, /*markSeen*/ returnTrue, host, preferences); }); const name = declaration && getNameOfDeclaration(declaration); @@ -219,13 +239,13 @@ function mapSuggestionDiagnostic(errorCode: number) { return errorCode; } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Node, errorCode: number, program: Program, cancellationToken: CancellationToken, markSeen: NodeSeenTracker, host: LanguageServiceHost, preferences: UserPreferences): Declaration | undefined { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, token: Node, errorCode: number, program: Program, cancellationToken: CancellationToken, markSeen: NodeSeenTracker, host: LanguageServiceHost, preferences: UserPreferences): Declaration | undefined { if (!isParameterPropertyModifier(token.kind) && token.kind !== SyntaxKind.Identifier && token.kind !== SyntaxKind.DotDotDotToken && token.kind !== SyntaxKind.ThisKeyword) { return undefined; } const { parent } = token; - const importAdder = createImportAdder(sourceFile, program, preferences, host); + const importAdder = createImportAdder(sourceFile, program, preferences, host, /*useAutoImportProvider*/ false); errorCode = mapSuggestionDiagnostic(errorCode); switch (errorCode) { // Variable and Property declarations @@ -302,7 +322,7 @@ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, to // Function 'this' case Diagnostics.this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation.code: - if (textChanges.isThisTypeAnnotatable(containingFunction) && markSeen(containingFunction)) { + if (isThisTypeAnnotatable(containingFunction) && markSeen(containingFunction)) { annotateThis(changes, sourceFile, containingFunction, program, host, cancellationToken); declaration = containingFunction; } @@ -317,7 +337,7 @@ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, to } function annotateVariableDeclaration( - changes: textChanges.ChangeTracker, + changes: ChangeTracker, importAdder: ImportAdder, sourceFile: SourceFile, declaration: VariableDeclaration | PropertyDeclaration | PropertySignature, @@ -331,7 +351,7 @@ function annotateVariableDeclaration( } function annotateParameters( - changes: textChanges.ChangeTracker, + changes: ChangeTracker, importAdder: ImportAdder, sourceFile: SourceFile, parameterDeclaration: ParameterDeclaration, @@ -362,7 +382,7 @@ function annotateParameters( } } -function annotateThis(changes: textChanges.ChangeTracker, sourceFile: SourceFile, containingFunction: textChanges.ThisTypeAnnotatable, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken) { +function annotateThis(changes: ChangeTracker, sourceFile: SourceFile, containingFunction: ThisTypeAnnotatable, program: Program, host: LanguageServiceHost, cancellationToken: CancellationToken) { const references = getFunctionReferences(containingFunction, sourceFile, program, cancellationToken); if (!references || !references.length) { return; @@ -381,14 +401,14 @@ function annotateThis(changes: textChanges.ChangeTracker, sourceFile: SourceFile } } -function annotateJSDocThis(changes: textChanges.ChangeTracker, sourceFile: SourceFile, containingFunction: SignatureDeclaration, typeNode: TypeNode) { +function annotateJSDocThis(changes: ChangeTracker, sourceFile: SourceFile, containingFunction: SignatureDeclaration, typeNode: TypeNode) { changes.addJSDocTags(sourceFile, containingFunction, [ factory.createJSDocThisTag(/*tagName*/ undefined, factory.createJSDocTypeExpression(typeNode)), ]); } function annotateSetAccessor( - changes: textChanges.ChangeTracker, + changes: ChangeTracker, importAdder: ImportAdder, sourceFile: SourceFile, setAccessorDeclaration: SetAccessorDeclaration, @@ -412,7 +432,7 @@ function annotateSetAccessor( } } -function annotate(changes: textChanges.ChangeTracker, importAdder: ImportAdder, sourceFile: SourceFile, declaration: textChanges.TypeAnnotatable, type: Type, program: Program, host: LanguageServiceHost): void { +function annotate(changes: ChangeTracker, importAdder: ImportAdder, sourceFile: SourceFile, declaration: TypeAnnotatable, type: Type, program: Program, host: LanguageServiceHost): void { const typeNode = getTypeNodeIfAccessible(type, declaration, program, host); if (typeNode) { if (isInJSFile(sourceFile) && declaration.kind !== SyntaxKind.PropertySignature) { @@ -432,9 +452,9 @@ function annotate(changes: textChanges.ChangeTracker, importAdder: ImportAdder, function tryReplaceImportTypeNodeWithAutoImport( typeNode: TypeNode, - declaration: textChanges.TypeAnnotatable, + declaration: TypeAnnotatable, sourceFile: SourceFile, - changes: textChanges.ChangeTracker, + changes: ChangeTracker, importAdder: ImportAdder, scriptTarget: ScriptTarget ): boolean { @@ -446,7 +466,7 @@ function tryReplaceImportTypeNodeWithAutoImport( return false; } -function annotateJSDocParameters(changes: textChanges.ChangeTracker, sourceFile: SourceFile, parameterInferences: readonly ParameterInference[], program: Program, host: LanguageServiceHost): void { +function annotateJSDocParameters(changes: ChangeTracker, sourceFile: SourceFile, parameterInferences: readonly ParameterInference[], program: Program, host: LanguageServiceHost): void { const signature = parameterInferences.length && parameterInferences[0].declaration.parent; if (!signature) { return; @@ -495,8 +515,8 @@ function annotateJSDocParameters(changes: textChanges.ChangeTracker, sourceFile: function getReferences(token: PropertyName | Token, program: Program, cancellationToken: CancellationToken): readonly Identifier[] { // Position shouldn't matter since token is not a SourceFile. - return mapDefined(FindAllReferences.getReferenceEntriesForNode(-1, token, program, program.getSourceFiles(), cancellationToken), entry => - entry.kind !== FindAllReferences.EntryKind.Span ? tryCast(entry.node, isIdentifier) : undefined); + return mapDefined(getReferenceEntriesForNode(-1, token, program, program.getSourceFiles(), cancellationToken), entry => + entry.kind !== EntryKind.Span ? tryCast(entry.node, isIdentifier) : undefined); } function inferTypeForVariableFromUsage(token: Identifier | PrivateIdentifier, program: Program, cancellationToken: CancellationToken): Type { diff --git a/src/services/codefixes/removeAccidentalCallParentheses.ts b/src/services/codefixes/removeAccidentalCallParentheses.ts index c4ad5c9ea3721..c2b864e09d76e 100644 --- a/src/services/codefixes/removeAccidentalCallParentheses.ts +++ b/src/services/codefixes/removeAccidentalCallParentheses.ts @@ -1,14 +1,12 @@ -import { - Diagnostics, - findAncestor, - getTokenAtPosition, - isCallExpression, - textChanges, -} from "../_namespaces/ts"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { isCallExpression } from "../../compiler/factory/nodeTests"; +import { findAncestor } from "../../compiler/utilitiesPublic"; import { createCodeFixActionWithoutFixAll, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "removeAccidentalCallParentheses"; const errorCodes = [ @@ -21,7 +19,7 @@ registerCodeFix({ if (!callExpression) { return undefined; } - const changes = textChanges.ChangeTracker.with(context, t => { + const changes = ChangeTracker.with(context, t => { t.deleteRange(context.sourceFile, { pos: callExpression.expression.end, end: callExpression.end }); }); return [createCodeFixActionWithoutFixAll(fixId, changes, Diagnostics.Remove_parentheses)]; diff --git a/src/services/codefixes/removeUnnecessaryAwait.ts b/src/services/codefixes/removeUnnecessaryAwait.ts index 45e6dcdef0dcd..ab80a8f760942 100644 --- a/src/services/codefixes/removeUnnecessaryAwait.ts +++ b/src/services/codefixes/removeUnnecessaryAwait.ts @@ -1,24 +1,28 @@ +import { tryCast } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; import { - AwaitKeyword, - Diagnostics, - findPrecedingToken, - getLeftmostExpression, - getTokenAtPosition, isAwaitExpression, isIdentifier, isParenthesizedExpression, +} from "../../compiler/factory/nodeTests"; +import { + AwaitKeyword, Node, SourceFile, SyntaxKind, - textChanges, TextSpan, - tryCast, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { getLeftmostExpression } from "../../compiler/utilities"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + findPrecedingToken, + getTokenAtPosition, +} from "../utilities"; const fixId = "removeUnnecessaryAwait"; const errorCodes = [ @@ -28,7 +32,7 @@ const errorCodes = [ registerCodeFix({ errorCodes, getCodeActions: function getCodeActionsToRemoveUnnecessaryAwait(context) { - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span)); + const changes = ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span)); if (changes.length > 0) { return [createCodeFixAction(fixId, changes, Diagnostics.Remove_unnecessary_await, fixId, Diagnostics.Remove_all_unnecessary_uses_of_await)]; } @@ -39,7 +43,7 @@ registerCodeFix({ }, }); -function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan) { +function makeChange(changeTracker: ChangeTracker, sourceFile: SourceFile, span: TextSpan) { const awaitKeyword = tryCast(getTokenAtPosition(sourceFile, span.start), (node): node is AwaitKeyword => node.kind === SyntaxKind.AwaitKeyword); const awaitExpression = awaitKeyword && tryCast(awaitKeyword.parent, isAwaitExpression); if (!awaitExpression) { diff --git a/src/services/codefixes/requireInTs.ts b/src/services/codefixes/requireInTs.ts index 940cc8b690f92..a83537e38231e 100644 --- a/src/services/codefixes/requireInTs.ts +++ b/src/services/codefixes/requireInTs.ts @@ -1,32 +1,38 @@ import { cast, - Debug, - Diagnostics, - factory, first, - getAllowSyntheticDefaultImports, - getTokenAtPosition, - Identifier, - ImportSpecifier, + tryCast, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isIdentifier, isObjectBindingPattern, - isRequireCall, isVariableDeclaration, isVariableStatement, +} from "../../compiler/factory/nodeTests"; +import { + Identifier, + ImportSpecifier, NamedImports, ObjectBindingPattern, Program, SourceFile, StringLiteralLike, - textChanges, - tryCast, VariableStatement, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getAllowSyntheticDefaultImports, + isRequireCall, +} from "../../compiler/utilities"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "requireInTs"; const errorCodes = [Diagnostics.require_call_may_be_converted_to_an_import.code]; @@ -37,7 +43,7 @@ registerCodeFix({ if (!info) { return undefined; } - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, info)); + const changes = ChangeTracker.with(context, t => doChange(t, context.sourceFile, info)); return [createCodeFixAction(fixId, changes, Diagnostics.Convert_require_to_import, fixId, Diagnostics.Convert_all_require_to_import)]; }, fixIds: [fixId], @@ -49,7 +55,7 @@ registerCodeFix({ }), }); -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info) { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, info: Info) { const { allowSyntheticDefaults, defaultImportName, namedImports, statement, required } = info; changes.replaceNode(sourceFile, statement, defaultImportName && !allowSyntheticDefaults ? factory.createImportEqualsDeclaration(/*modifiers*/ undefined, /*isTypeOnly*/ false, defaultImportName, factory.createExternalModuleReference(required)) diff --git a/src/services/codefixes/returnValueCorrect.ts b/src/services/codefixes/returnValueCorrect.ts index 2c16bfa5b2143..82c36909d5340 100644 --- a/src/services/codefixes/returnValueCorrect.ts +++ b/src/services/codefixes/returnValueCorrect.ts @@ -1,50 +1,64 @@ import { append, - ArrowFunction, - CodeFixContext, - copyComments, - createSymbolTable, - Debug, - Diagnostics, - Expression, - factory, - findAncestor, first, - FunctionLikeDeclaration, - getTokenAtPosition, - hasSyntacticModifier, - Identifier, + length, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, isBlock, isCallExpression, - isDeclarationName, isExpressionStatement, - isFunctionLikeDeclaration, isJsxAttribute, isJsxExpression, isLabeledStatement, - isVariableLike, - length, +} from "../../compiler/factory/nodeTests"; +import { + ArrowFunction, + Expression, + FunctionLikeDeclaration, + Identifier, ModifierFlags, - needsParentheses, Node, - probablyUsesSemicolons, - rangeContainsRange, SourceFile, Statement, - suppressLeadingAndTrailingTrivia, SymbolFlags, SyntaxKind, - textChanges, Type, TypeChecker, VariableLikeDeclaration, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + createSymbolTable, + hasSyntacticModifier, + isDeclarationName, + isVariableLike, +} from "../../compiler/utilities"; +import { + findAncestor, + isFunctionLikeDeclaration, +} from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { + ChangeTracker, + LeadingTriviaOption, + TrailingTriviaOption, +} from "../textChanges"; +import { CodeFixContext } from "../types"; +import { + copyComments, + getTokenAtPosition, + needsParentheses, + probablyUsesSemicolons, + rangeContainsRange, + suppressLeadingAndTrailingTrivia, +} from "../utilities"; const fixId = "returnValueCorrect"; const fixIdAddReturnStatement = "fixAddReturnStatement"; @@ -250,17 +264,17 @@ function getVariableLikeInitializer(declaration: VariableLikeDeclaration): Expre } } -function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, expression: Expression, statement: Statement) { +function addReturnStatement(changes: ChangeTracker, sourceFile: SourceFile, expression: Expression, statement: Statement) { suppressLeadingAndTrailingTrivia(expression); const probablyNeedSemi = probablyUsesSemicolons(sourceFile); changes.replaceNode(sourceFile, statement, factory.createReturnStatement(expression), { - leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, - trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude, + leadingTriviaOption: LeadingTriviaOption.Exclude, + trailingTriviaOption: TrailingTriviaOption.Exclude, suffix: probablyNeedSemi ? ";" : undefined }); } -function removeBlockBodyBrace(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression, commentSource: Node, withParen: boolean) { +function removeBlockBodyBrace(changes: ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression, commentSource: Node, withParen: boolean) { const newBody = (withParen || needsParentheses(expression)) ? factory.createParenthesizedExpression(expression) : expression; suppressLeadingAndTrailingTrivia(commentSource); copyComments(commentSource, newBody); @@ -268,21 +282,21 @@ function removeBlockBodyBrace(changes: textChanges.ChangeTracker, sourceFile: So changes.replaceNode(sourceFile, declaration.body, newBody); } -function wrapBlockWithParen(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression) { +function wrapBlockWithParen(changes: ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression) { changes.replaceNode(sourceFile, declaration.body, factory.createParenthesizedExpression(expression)); } function getActionForfixAddReturnStatement(context: CodeFixContext, expression: Expression, statement: Statement) { - const changes = textChanges.ChangeTracker.with(context, t => addReturnStatement(t, context.sourceFile, expression, statement)); + const changes = ChangeTracker.with(context, t => addReturnStatement(t, context.sourceFile, expression, statement)); return createCodeFixAction(fixId, changes, Diagnostics.Add_a_return_statement, fixIdAddReturnStatement, Diagnostics.Add_all_missing_return_statement); } function getActionForFixRemoveBracesFromArrowFunctionBody(context: CodeFixContext, declaration: ArrowFunction, expression: Expression, commentSource: Node) { - const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, expression, commentSource, /*withParen*/ false)); + const changes = ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, expression, commentSource, /*withParen*/ false)); return createCodeFixAction(fixId, changes, Diagnostics.Remove_braces_from_arrow_function_body, fixRemoveBracesFromArrowFunctionBody, Diagnostics.Remove_braces_from_all_arrow_function_bodies_with_relevant_issues); } function getActionForfixWrapTheBlockWithParen(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { - const changes = textChanges.ChangeTracker.with(context, t => wrapBlockWithParen(t, context.sourceFile, declaration, expression)); + const changes = ChangeTracker.with(context, t => wrapBlockWithParen(t, context.sourceFile, declaration, expression)); return createCodeFixAction(fixId, changes, Diagnostics.Wrap_the_following_body_with_parentheses_which_should_be_an_object_literal, fixIdWrapTheBlockWithParen, Diagnostics.Wrap_all_object_literal_with_parentheses); } diff --git a/src/services/codefixes/splitTypeOnlyImport.ts b/src/services/codefixes/splitTypeOnlyImport.ts index 6af08a70ebff6..d088a1fda3d01 100644 --- a/src/services/codefixes/splitTypeOnlyImport.ts +++ b/src/services/codefixes/splitTypeOnlyImport.ts @@ -1,21 +1,21 @@ +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isImportDeclaration } from "../../compiler/factory/nodeTests"; import { - CodeFixContextBase, - Debug, - Diagnostics, - factory, - findAncestor, - getTokenAtPosition, ImportDeclaration, - isImportDeclaration, SourceFile, - textChanges, TextSpan, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { findAncestor } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { CodeFixContextBase } from "../types"; +import { getTokenAtPosition } from "../utilities"; const errorCodes = [Diagnostics.A_type_only_import_can_specify_a_default_import_or_named_bindings_but_not_both.code]; const fixId = "splitTypeOnlyImport"; @@ -23,7 +23,7 @@ registerCodeFix({ errorCodes, fixIds: [fixId], getCodeActions: function getCodeActionsToSplitTypeOnlyImport(context) { - const changes = textChanges.ChangeTracker.with(context, t => { + const changes = ChangeTracker.with(context, t => { return splitTypeOnlyImport(t, getImportDeclaration(context.sourceFile, context.span), context); }); if (changes.length) { @@ -39,7 +39,7 @@ function getImportDeclaration(sourceFile: SourceFile, span: TextSpan) { return findAncestor(getTokenAtPosition(sourceFile, span.start), isImportDeclaration); } -function splitTypeOnlyImport(changes: textChanges.ChangeTracker, importDeclaration: ImportDeclaration | undefined, context: CodeFixContextBase) { +function splitTypeOnlyImport(changes: ChangeTracker, importDeclaration: ImportDeclaration | undefined, context: CodeFixContextBase) { if (!importDeclaration) { return; } diff --git a/src/services/codefixes/useBigintLiteral.ts b/src/services/codefixes/useBigintLiteral.ts index 4492e176b5c57..7cfc8482ff1a9 100644 --- a/src/services/codefixes/useBigintLiteral.ts +++ b/src/services/codefixes/useBigintLiteral.ts @@ -1,18 +1,18 @@ +import { tryCast } from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isNumericLiteral } from "../../compiler/factory/nodeTests"; import { - Diagnostics, - factory, - getTokenAtPosition, - isNumericLiteral, SourceFile, - textChanges, TextSpan, - tryCast, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixId = "useBigintLiteral"; const errorCodes = [ @@ -22,7 +22,7 @@ const errorCodes = [ registerCodeFix({ errorCodes, getCodeActions: function getCodeActionsToUseBigintLiteral(context) { - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span)); + const changes = ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span)); if (changes.length > 0) { return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_a_bigint_numeric_literal, fixId, Diagnostics.Convert_all_to_bigint_numeric_literals)]; } @@ -33,7 +33,7 @@ registerCodeFix({ }, }); -function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan) { +function makeChange(changeTracker: ChangeTracker, sourceFile: SourceFile, span: TextSpan) { const numericLiteral = tryCast(getTokenAtPosition(sourceFile, span.start), isNumericLiteral); if (!numericLiteral) { return; @@ -41,6 +41,5 @@ function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: Source // We use .getText to overcome parser inaccuracies: https://github.com/microsoft/TypeScript/issues/33298 const newText = numericLiteral.getText(sourceFile) + "n"; - changeTracker.replaceNode(sourceFile, numericLiteral, factory.createBigIntLiteral(newText)); } diff --git a/src/services/codefixes/useDefaultImport.ts b/src/services/codefixes/useDefaultImport.ts index 75f72964530b6..459d06b5a23ac 100644 --- a/src/services/codefixes/useDefaultImport.ts +++ b/src/services/codefixes/useDefaultImport.ts @@ -1,24 +1,28 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; import { - AnyImportSyntax, - Diagnostics, - Expression, - getQuotePreference, - getTokenAtPosition, - Identifier, isExternalModuleReference, isIdentifier, isImportEqualsDeclaration, isNamespaceImport, - makeImport, +} from "../../compiler/factory/nodeTests"; +import { + AnyImportSyntax, + Expression, + Identifier, SourceFile, - textChanges, UserPreferences, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { + getQuotePreference, + getTokenAtPosition, + makeImport, +} from "../utilities"; const fixId = "useDefaultImport"; const errorCodes = [Diagnostics.Import_may_be_converted_to_a_default_import.code]; @@ -28,7 +32,7 @@ registerCodeFix({ const { sourceFile, span: { start } } = context; const info = getInfo(sourceFile, start); if (!info) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, info, context.preferences)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, info, context.preferences)); return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_default_import, fixId, Diagnostics.Convert_all_to_default_imports)]; }, fixIds: [fixId], @@ -56,6 +60,6 @@ function getInfo(sourceFile: SourceFile, pos: number): Info | undefined { } } -function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info, preferences: UserPreferences): void { +function doChange(changes: ChangeTracker, sourceFile: SourceFile, info: Info, preferences: UserPreferences): void { changes.replaceNode(sourceFile, info.importNode, makeImport(info.name, /*namedImports*/ undefined, info.moduleSpecifier, getQuotePreference(sourceFile, preferences))); } diff --git a/src/services/codefixes/wrapJsxInFragment.ts b/src/services/codefixes/wrapJsxInFragment.ts index 78122a0840c62..55ed9d17399c2 100644 --- a/src/services/codefixes/wrapJsxInFragment.ts +++ b/src/services/codefixes/wrapJsxInFragment.ts @@ -1,22 +1,22 @@ +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isBinaryExpression } from "../../compiler/factory/nodeTests"; import { BinaryExpression, - Diagnostics, - factory, - getTokenAtPosition, - isBinaryExpression, - isJsxChild, JsxChild, Node, - nodeIsMissing, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { nodeIsMissing } from "../../compiler/utilities"; +import { isJsxChild } from "../../compiler/utilitiesPublic"; import { codeFixAll, createCodeFixAction, registerCodeFix, -} from "../_namespaces/ts.codefix"; +} from "../codeFixProvider"; +import { ChangeTracker } from "../textChanges"; +import { getTokenAtPosition } from "../utilities"; const fixID = "wrapJsxInFragment"; const errorCodes = [Diagnostics.JSX_expressions_must_have_one_parent_element.code]; @@ -26,7 +26,7 @@ registerCodeFix({ const { sourceFile, span } = context; const node = findNodeToFix(sourceFile, span.start); if (!node) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node)); + const changes = ChangeTracker.with(context, t => doChange(t, sourceFile, node)); return [createCodeFixAction(fixID, changes, Diagnostics.Wrap_in_JSX_fragment, fixID, Diagnostics.Wrap_all_unparented_JSX_in_JSX_fragment)]; }, fixIds: [fixID], @@ -52,7 +52,7 @@ function findNodeToFix(sourceFile: SourceFile, pos: number): BinaryExpression | return binaryExpr; } -function doChange(changeTracker: textChanges.ChangeTracker, sf: SourceFile, node: Node) { +function doChange(changeTracker: ChangeTracker, sf: SourceFile, node: Node) { const jsx = flattenInvalidBinaryExpr(node); if (jsx) changeTracker.replaceNode(sf, node, factory.createJsxFragment(factory.createJsxOpeningFragment(), jsx, factory.createJsxJsxClosingFragment())); } diff --git a/src/services/completions.ts b/src/services/completions.ts index 75ec22e3528e4..fa93409437ff0 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1,362 +1,201 @@ import { - __String, - addToSeen, append, - BinaryExpression, - BindingElement, - BindingPattern, - BreakOrContinueStatement, - CancellationToken, - canUsePropertyAccess, - CaseBlock, cast, - CharacterCodes, - ClassElement, - CodeAction, - codefix, - compareNumberOfDirectorySeparators, compareStringsCaseSensitiveUI, compareTextSpans, - Comparison, - CompilerOptions, - compilerOptionsIndicateEsModules, - CompletionEntry, - CompletionEntryData, - CompletionEntryDataAutoImport, - CompletionEntryDataResolved, - CompletionEntryDataUnresolved, - CompletionEntryDetails, - CompletionEntryLabelDetails, - CompletionInfo, - CompletionInfoFlags, - CompletionsTriggerCharacter, - CompletionTriggerKind, concatenate, - ConstructorDeclaration, - ContextFlags, countWhere, - createModuleSpecifierResolutionHost, - createPackageJsonImportFilter, - createPrinter, createSortedArray, - createTextSpanFromBounds, - createTextSpanFromNode, - createTextSpanFromRange, - Debug, - Declaration, - Diagnostics, - diagnosticToString, - displayPart, - DotDotDotToken, - EmitFlags, - EmitHint, - EmitTextWriter, - EntityName, - EnumMember, - escapeSnippetText, every, - ExportKind, - Expression, - ExpressionWithTypeArguments, - factory, filter, find, - findAncestor, - findChildOfKind, - findPrecedingToken, first, firstDefined, flatMap, forEach, - formatting, - FunctionLikeDeclaration, - getAllSuperTypeNodes, - getAncestor, - getCombinedLocalAndExportSymbolFlags, - getContainingClass, - getContextualTypeFromParent, - getDeclarationModifierFlagsFromSymbol, - getEffectiveBaseTypeNode, - getEffectiveModifierFlags, - getEffectiveTypeAnnotationNode, - getEmitModuleResolutionKind, - getEmitScriptTarget, - getEscapedTextOfIdentifierOrLiteral, - getEscapedTextOfJsxAttributeName, - getExportInfoMap, - getFormatCodeSettingsForWriting, - getJSDocParameterTags, - getLanguageVariant, - getLeftmostAccessExpression, - getLineAndCharacterOfPosition, - getLineStartPositionForPosition, - getLocalSymbolForExportDefault, - getNameOfDeclaration, - getNameTable, - getNewLineCharacter, - getNewLineKind, - getNewLineOrDefaultFromHost, - getPropertyNameForPropertyNameNode, - getQuotePreference, - getReplacementSpanForContextToken, - getRootDeclaration, - getSourceFileOfModule, - getSwitchedType, - getSymbolId, - getSynthesizedDeepClone, - getTokenAtPosition, - getTouchingPropertyName, - hasDocComment, - hasEffectiveModifier, - hasInitializer, - hasType, - Identifier, - identifierToKeywordKind, - ImportDeclaration, - ImportEqualsDeclaration, - ImportKind, - ImportOrExportSpecifier, - ImportSpecifier, - ImportTypeNode, - IncompleteCompletionsCache, - IndexedAccessTypeNode, insertSorted, - InternalSymbolName, - isAbstractConstructorSymbol, + isString, + last, + lastOrUndefined, + length, + map, + mapDefined, + memoize, + memoizeOne, + or, + singleElementArray, + some, + stableSort, + startsWith, + tryCast, +} from "../compiler/core"; +import { + Comparison, + SortedArray, +} from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { Diagnostics } from "../compiler/diagnosticInformationMap.generated"; +import { createPrinter } from "../compiler/emitter"; +import { setEmitFlags, setSnippetElement } from "../compiler/factory/emitNode"; +import { factory } from "../compiler/factory/nodeFactory"; +import { isArrowFunction, - isAssertionExpression, isAwaitExpression, isBigIntLiteral, isBinaryExpression, isBindingElement, - isBindingPattern, - isBreakOrContinueStatement, isCallExpression, isCaseBlock, isCaseClause, isCaseKeyword, - isCheckJsEnabledForFile, - isClassElement, - isClassLike, - isClassMemberModifier, - isClassOrTypeElement, isClassStaticBlockDeclaration, isComputedPropertyName, isConstructorDeclaration, - isContextualKeyword, - isDeclarationName, - isDeprecatedDeclaration, - isEntityName, isEnumMember, - isEqualityOperatorKind, isExportAssignment, isExportDeclaration, - isExpression, - isExternalModuleNameRelative, isExternalModuleReference, - isExternalModuleSymbol, - isFunctionBlock, - isFunctionLike, - isFunctionLikeDeclaration, - isFunctionLikeKind, isFunctionTypeNode, isIdentifier, - isIdentifierText, - isImportableFile, isImportDeclaration, isImportEqualsDeclaration, isImportKeyword, isImportSpecifier, - isInComment, isIndexSignatureDeclaration, isInferTypeNode, - isInitializedProperty, - isInJSFile, - isInRightSideOfInternalImportEqualsDeclaration, - isInString, isInterfaceDeclaration, isIntersectionTypeNode, isJSDoc, isJSDocAugmentsTag, isJSDocImplementsTag, isJSDocParameterTag, - isJSDocTag, isJSDocTemplateTag, isJsxAttribute, isJsxClosingElement, isJsxElement, isJsxExpression, isJsxFragment, - isJsxOpeningLikeElement, isJsxSpreadAttribute, - isKeyword, - isKnownSymbol, isLabeledStatement, - isLiteralImportTypeNode, - isMemberName, isMethodDeclaration, - isModifier, - isModifierKind, isModuleDeclaration, isNamedExports, isNamedImports, - isNamedImportsOrExports, isNamespaceImport, - isNodeDescendantOf, isObjectBindingPattern, isObjectLiteralExpression, - isObjectTypeDeclaration, isParameter, - isParameterPropertyModifier, - isPartOfTypeNode, - isPossiblyTypeArgumentPosition, isPrivateIdentifier, - isPrivateIdentifierClassElementDeclaration, isPropertyAccessExpression, isPropertyDeclaration, - isPropertyNameLiteral, isRegularExpressionLiteral, isShorthandPropertyAssignment, - isSingleOrDoubleQuote, isSourceFile, - isSourceFileJS, isSpreadAssignment, - isStatement, - isStatic, - isString, - isStringAndEmptyAnonymousObjectIntersection, - isStringANonContextualKeyword, - isStringLiteralLike, - isStringLiteralOrTemplate, - isStringTextContainingNode, isSyntaxList, - isTypeKeyword, - isTypeKeywordTokenOrIdentifier, isTypeLiteralNode, - isTypeNode, isTypeOfExpression, - isTypeOnlyImportDeclaration, - isTypeOnlyImportOrExportDeclaration, isTypeParameterDeclaration, - isTypeReferenceType, - isValidTypeOnlyAliasUseSite, isVariableDeclaration, - isVariableLike, - JsDoc, +} from "../compiler/factory/nodeTests"; +import { timestamp } from "../compiler/performanceCore"; +import { + getLineAndCharacterOfPosition, + isIdentifierText, + stringToToken, + tokenToString, +} from "../compiler/scanner"; +import { isInitializedProperty } from "../compiler/transformers/utilities"; +import { + __String, + BinaryExpression, + BindingElement, + BindingPattern, + BreakOrContinueStatement, + CancellationToken, + CaseBlock, + CharacterCodes, + ClassElement, + CompilerOptions, + ConstructorDeclaration, + ContextFlags, + Declaration, + DotDotDotToken, + EmitFlags, + EmitHint, + EmitTextWriter, + EntityName, + EnumMember, + Expression, + ExpressionWithTypeArguments, + FunctionLikeDeclaration, + Identifier, + ImportDeclaration, + ImportEqualsDeclaration, + ImportOrExportSpecifier, + ImportSpecifier, + ImportTypeNode, + IndexedAccessTypeNode, + InternalSymbolName, JSDocParameterTag, JSDocPropertyTag, JSDocReturnTag, JSDocSatisfiesTag, JSDocTag, - JSDocTagInfo, JSDocTemplateTag, JSDocThrowsTag, JSDocTypedefTag, JSDocTypeExpression, JSDocTypeTag, - JsTyping, JsxAttribute, JsxAttributes, JsxClosingElement, JsxElement, JsxOpeningLikeElement, JsxSpreadAttribute, - LanguageServiceHost, LanguageVariant, - last, - lastOrUndefined, - length, ListFormat, LiteralType, LiteralTypeNode, - map, - mapDefined, MemberOverrideStatus, - memoize, - memoizeOne, MethodDeclaration, ModifierFlags, - modifiersToFlags, ModifierSyntaxKind, - modifierToFlag, ModuleDeclaration, ModuleReference, - moduleResolutionSupportsPackageJsonExportsAndImports, NamedImportBindings, - newCaseClauseTracker, Node, NodeArray, NodeBuilderFlags, NodeFlags, - nodeIsMissing, NumericLiteral, ObjectBindingPattern, ObjectLiteralExpression, ObjectType, ObjectTypeDeclaration, - or, ParameterDeclaration, ParenthesizedTypeNode, - positionBelongsToNode, - positionIsASICandidate, - positionsAreOnSameLine, PrinterOptions, - probablyUsesSemicolons, Program, - programContainsModules, PropertyAccessExpression, PropertyDeclaration, PropertyName, PropertySignature, PseudoBigInt, - pseudoBigIntToString, QualifiedName, - quote, - QuotePreference, - rangeContainsPosition, - rangeContainsPositionExclusive, - rangeIsOnSingleLine, - ScriptElementKind, - ScriptElementKindModifier, ScriptTarget, - SemanticMeaning, - setEmitFlags, - setSnippetElement, - shouldUseUriStyleNodeCoreModules, - SignatureHelp, SignatureKind, - singleElementArray, - skipAlias, SnippetKind, - some, - SortedArray, SourceFile, SpreadAssignment, - stableSort, - startsWith, - stringToToken, - stripQuotes, Symbol, - SymbolDisplay, - SymbolDisplayPart, - SymbolDisplayPartKind, - SymbolExportInfo, SymbolFlags, SymbolId, SyntaxKind, - TextChange, - textChanges, - textPart, TextRange, TextSpan, - ThisContainer, - timestamp, Token, TokenSyntaxKind, - tokenToString, - tryCast, - tryGetImportFromModuleSpecifier, - tryGetTextOfPropertyName, Type, TypeChecker, TypeElement, @@ -367,14 +206,223 @@ import { TypeParameterDeclaration, TypeQueryNode, TypeReferenceNode, - unescapeLeadingUnderscores, UnionReduction, UnionType, UserPreferences, VariableDeclaration, +} from "../compiler/types"; +import { + addToSeen, + canUsePropertyAccess, + compareNumberOfDirectorySeparators, + escapeSnippetText, + getAllSuperTypeNodes, + getAncestor, + getCombinedLocalAndExportSymbolFlags, + getContainingClass, + getDeclarationModifierFlagsFromSymbol, + getEffectiveBaseTypeNode, + getEffectiveModifierFlags, + getEffectiveTypeAnnotationNode, + getEmitModuleResolutionKind, + getEmitScriptTarget, + getEscapedTextOfIdentifierOrLiteral, + getEscapedTextOfJsxAttributeName, + getLanguageVariant, + getLeftmostAccessExpression, + getLocalSymbolForExportDefault, + getNewLineCharacter, + getPropertyNameForPropertyNameNode, + getRootDeclaration, + getSourceFileOfModule, + getSymbolId, + hasEffectiveModifier, + isAbstractConstructorSymbol, + isCheckJsEnabledForFile, + isContextualKeyword, + isDeclarationName, + isFunctionBlock, + isInJSFile, + isKeyword, + isKnownSymbol, + isLiteralImportTypeNode, + isNamedImportsOrExports, + isNodeDescendantOf, + isObjectTypeDeclaration, + isPartOfTypeNode, + isPropertyNameLiteral, + isSingleOrDoubleQuote, + isSourceFileJS, + isStatic, + isStringANonContextualKeyword, + isValidTypeOnlyAliasUseSite, + isVariableLike, + modifiersToFlags, + modifierToFlag, + moduleResolutionSupportsPackageJsonExportsAndImports, + nodeIsMissing, + positionsAreOnSameLine, + pseudoBigIntToString, + rangeIsOnSingleLine, + skipAlias, + stripQuotes, + ThisContainer, + tryGetImportFromModuleSpecifier, + tryGetTextOfPropertyName, walkUpParenthesizedExpressions, -} from "./_namespaces/ts"; -import { StringCompletions } from "./_namespaces/ts.Completions"; +} from "../compiler/utilities"; +import { + createTextSpanFromBounds, + findAncestor, + getJSDocParameterTags, + getNameOfDeclaration, + hasInitializer, + hasType, + identifierToKeywordKind, + isAssertionExpression, + isBindingPattern, + isBreakOrContinueStatement, + isClassElement, + isClassLike, + isClassMemberModifier, + isClassOrTypeElement, + isEntityName, + isExpression, + isExternalModuleNameRelative, + isFunctionLike, + isFunctionLikeDeclaration, + isFunctionLikeKind, + isJSDocTag, + isJsxOpeningLikeElement, + isMemberName, + isModifier, + isModifierKind, + isParameterPropertyModifier, + isPrivateIdentifierClassElementDeclaration, + isStatement, + isStringLiteralLike, + isStringTextContainingNode, + isTypeNode, + isTypeOnlyImportDeclaration, + isTypeOnlyImportOrExportDeclaration, + isTypeReferenceType, + unescapeLeadingUnderscores, +} from "../compiler/utilitiesPublic"; +import * as JsTyping from "../jsTyping/jsTyping"; +import { + addNewNodeForMemberSymbol, + getNoopSymbolTrackerWithResolver, + PreserveOptionalFlags, + typeToAutoImportableTypeNode, +} from "./codefixes/helpers"; +import { + createImportAdder, + createImportSpecifierResolver, + getImportCompletionAction, + getImportKind, + getPromoteTypeOnlyCompletionAction, + ImportAdder, + ImportSpecifierResolver, +} from "./codefixes/importAdder"; +import { + getExportInfoMap, + ImportKind, + isImportableFile, +} from "./exportInfoMap"; +import { formatNodeGivenIndentation } from "./formatting/formatting"; +import { + getJSDocParameterNameCompletionDetails, + getJSDocParameterNameCompletions, + getJSDocTagCompletionDetails, + getJSDocTagCompletions, + getJSDocTagNameCompletionDetails, + getJSDocTagNameCompletions, +} from "./jsDoc"; +import { getNameTable } from "./services"; +import { getArgumentInfoForCompletions } from "./signatureHelp"; +import * as StringCompletions from "./stringCompletions"; +import { + getSymbolDisplayPartsDocumentationAndSymbolKind, + getSymbolKind, + getSymbolModifiers, +} from "./symbolDisplay"; +import { + applyChanges, + assignPositionsToNode, + ChangeTracker, + createWriter, +} from "./textChanges"; +import { + CodeAction, + CompletionEntry, + CompletionEntryData, + CompletionEntryDataAutoImport, + CompletionEntryDataResolved, + CompletionEntryDataUnresolved, + CompletionEntryDetails, + CompletionEntryLabelDetails, + CompletionInfo, + CompletionInfoFlags, + CompletionsTriggerCharacter, + CompletionTriggerKind, + ExportKind, + FormatContext, + IncompleteCompletionsCache, + JSDocTagInfo, + LanguageServiceHost, + ScriptElementKind, + ScriptElementKindModifier, + SymbolDisplayPart, + SymbolDisplayPartKind, + SymbolExportInfo, + TextChange, +} from "./types"; +import { + compilerOptionsIndicateEsModules, + createModuleSpecifierResolutionHost, + createPackageJsonImportFilter, + createTextSpanFromNode, + createTextSpanFromRange, + diagnosticToString, + displayPart, + findChildOfKind, + findPrecedingToken, + getContextualTypeFromParent, + getFormatCodeSettingsForWriting, + getLineStartPositionForPosition, + getNewLineKind, + getNewLineOrDefaultFromHost, + getQuotePreference, + getReplacementSpanForContextToken, + getSwitchedType, + getSynthesizedDeepClone, + getTokenAtPosition, + getTouchingPropertyName, + hasDocComment, + isDeprecatedDeclaration, + isEqualityOperatorKind, + isExternalModuleSymbol, + isInComment, + isInRightSideOfInternalImportEqualsDeclaration, + isInString, + isPossiblyTypeArgumentPosition, + isStringAndEmptyAnonymousObjectIntersection, + isStringLiteralOrTemplate, + isTypeKeyword, + isTypeKeywordTokenOrIdentifier, + newCaseClauseTracker, + positionBelongsToNode, + positionIsASICandidate, + probablyUsesSemicolons, + programContainsModules, + quote, + QuotePreference, + rangeContainsPosition, + rangeContainsPositionExclusive, + SemanticMeaning, + shouldUseUriStyleNodeCoreModules, + textPart, +} from "./utilities"; // Exported only for tests /** @internal */ @@ -593,7 +641,7 @@ type ModuleSpecifierResolutionResult = "skipped" | "failed" | { function resolvingModuleSpecifiers( logPrefix: string, host: LanguageServiceHost, - resolver: codefix.ImportSpecifierResolver, + resolver: ImportSpecifierResolver, program: Program, position: number, preferences: UserPreferences, @@ -666,7 +714,7 @@ export function getCompletionsAtPosition( triggerCharacter: CompletionsTriggerCharacter | undefined, completionKind: CompletionTriggerKind | undefined, cancellationToken: CancellationToken, - formatContext?: formatting.FormatContext, + formatContext?: FormatContext, includeSymbol = false ): CompletionInfo | undefined { const { previousToken } = getRelevantTokens(position, sourceFile); @@ -723,7 +771,7 @@ export function getCompletionsAtPosition( case CompletionDataKind.JsDocTagName: // If the current position is a jsDoc tag name, only tag names should be provided for completion return jsdocCompletionInfo([ - ...JsDoc.getJSDocTagNameCompletions(), + ...getJSDocTagNameCompletions(), ...getJSDocParameterCompletions( sourceFile, position, @@ -734,7 +782,7 @@ export function getCompletionsAtPosition( case CompletionDataKind.JsDocTag: // If the current position is a jsDoc tag, only tags should be provided for completion return jsdocCompletionInfo([ - ...JsDoc.getJSDocTagCompletions(), + ...getJSDocTagCompletions(), ...getJSDocParameterCompletions( sourceFile, position, @@ -743,7 +791,7 @@ export function getCompletionsAtPosition( preferences, /*tagNameOnly*/ false)]); case CompletionDataKind.JsDocParameterName: - return jsdocCompletionInfo(JsDoc.getJSDocParameterNameCompletions(completionData.tag)); + return jsdocCompletionInfo(getJSDocParameterNameCompletions(completionData.tag)); case CompletionDataKind.Keywords: return specificKeywordCompletionInfo(completionData.keywordCompletions, completionData.isNewIdentifierLocation); default: @@ -800,7 +848,7 @@ function continuePreviousIncompleteResponse( const newEntries = resolvingModuleSpecifiers( "continuePreviousIncompleteResponse", host, - codefix.createImportSpecifierResolver(file, program, host, preferences), + createImportSpecifierResolver(file, program, host, preferences), program, location.getStart(), preferences, @@ -1199,7 +1247,7 @@ function completionInfoFromData( log: Log, completionData: CompletionData, preferences: UserPreferences, - formatContext: formatting.FormatContext | undefined, + formatContext: FormatContext | undefined, position: number, includeSymbol: boolean | undefined, ): CompletionInfo | undefined { @@ -1349,7 +1397,7 @@ function getExhaustiveCaseSnippets( options: CompilerOptions, host: LanguageServiceHost, program: Program, - formatContext: formatting.FormatContext | undefined): { entry: CompletionEntry, importAdder: codefix.ImportAdder } | undefined { + formatContext: FormatContext | undefined): { entry: CompletionEntry, importAdder: ImportAdder } | undefined { const clauses = caseBlock.clauses; const checker = program.getTypeChecker(); @@ -1360,7 +1408,7 @@ function getExhaustiveCaseSnippets( const target = getEmitScriptTarget(options); const quotePreference = getQuotePreference(sourceFile, preferences); - const importAdder = codefix.createImportAdder(sourceFile, program, preferences, host); + const importAdder = createImportAdder(sourceFile, program, preferences, host, /*useAutoImportProvider*/ false); const elements: Expression[] = []; for (const type of switchType.types as LiteralType[]) { // Enums @@ -1375,7 +1423,7 @@ function getExhaustiveCaseSnippets( } tracker.addValue(enumValue); } - const typeNode = codefix.typeToAutoImportableTypeNode(checker, importAdder, type, caseBlock, target); + const typeNode = typeToAutoImportableTypeNode(checker, importAdder, type, caseBlock, target); if (!typeNode) { return undefined; } @@ -1603,7 +1651,7 @@ function createCompletionEntry( options: CompilerOptions, preferences: UserPreferences, completionKind: CompletionKind, - formatContext: formatting.FormatContext | undefined, + formatContext: FormatContext | undefined, isJsxIdentifierExpected: boolean | undefined, isRightOfOpenTag: boolean | undefined, includeSymbol: boolean @@ -1748,8 +1796,8 @@ function createCompletionEntry( // entries (like JavaScript identifier entries). return { name, - kind: SymbolDisplay.getSymbolKind(typeChecker, symbol, location), - kindModifiers: SymbolDisplay.getSymbolModifiers(typeChecker, symbol), + kind: getSymbolKind(typeChecker, symbol, location), + kindModifiers: getSymbolModifiers(typeChecker, symbol), sortText, source, hasAction: hasAction ? true : undefined, @@ -1825,8 +1873,8 @@ function getEntryForMemberCompletion( location: Node, position: number, contextToken: Node | undefined, - formatContext: formatting.FormatContext | undefined, -): { insertText: string, isSnippet?: true, importAdder?: codefix.ImportAdder, replacementSpan?: TextSpan } { + formatContext: FormatContext | undefined, +): { insertText: string, isSnippet?: true, importAdder?: ImportAdder, replacementSpan?: TextSpan } { const classLikeDeclaration = findAncestor(location, isClassLike); if (!classLikeDeclaration) { return { insertText: name }; @@ -1845,7 +1893,7 @@ function getEntryForMemberCompletion( omitTrailingSemicolon: false, newLine: getNewLineKind(getNewLineOrDefaultFromHost(host, formatContext?.options)), }); - const importAdder = codefix.createImportAdder(sourceFile, program, preferences, host); + const importAdder = createImportAdder(sourceFile, program, preferences, host, /*useAutoImportProvider*/ false); // Create empty body for possible method implementation. let body; @@ -1868,7 +1916,7 @@ function getEntryForMemberCompletion( const { modifiers: presentModifiers, span: modifiersSpan } = getPresentModifiers(contextToken, sourceFile, position); const isAbstract = !!(presentModifiers & ModifierFlags.Abstract); const completionNodes: Node[] = []; - codefix.addNewNodeForMemberSymbol( + addNewNodeForMemberSymbol( symbol, classLikeDeclaration, sourceFile, @@ -1902,7 +1950,7 @@ function getEntryForMemberCompletion( completionNodes.push(node); }, body, - codefix.PreserveOptionalFlags.Property, + PreserveOptionalFlags.Property, isAbstract); if (completionNodes.length) { @@ -1991,7 +2039,7 @@ function getEntryForObjectLiteralMethodCompletion( host: LanguageServiceHost, options: CompilerOptions, preferences: UserPreferences, - formatContext: formatting.FormatContext | undefined, + formatContext: FormatContext | undefined, ): { insertText: string, isSnippet?: true, labelDetails: CompletionEntryLabelDetails } | undefined { const isSnippet = preferences.includeCompletionsWithSnippetText || undefined; let insertText: string = name; @@ -2080,7 +2128,7 @@ function createObjectLiteralMethod( // We don't support overloads in object literals. return undefined; } - const typeNode = checker.typeToTypeNode(effectiveType, enclosingDeclaration, builderFlags, codefix.getNoopSymbolTrackerWithResolver({ program, host })); + const typeNode = checker.typeToTypeNode(effectiveType, enclosingDeclaration, builderFlags, getNoopSymbolTrackerWithResolver({ program, host })); if (!typeNode || !isFunctionTypeNode(typeNode)) { return undefined; } @@ -2123,7 +2171,7 @@ function createSnippetPrinter( printerOptions: PrinterOptions, ) { let escapes: TextChange[] | undefined; - const baseWriter = textChanges.createWriter(getNewLineCharacter(printerOptions)); + const baseWriter = createWriter(getNewLineCharacter(printerOptions)); const printer = createPrinter(printerOptions, baseWriter); const writer: EmitTextWriter = { ...baseWriter, @@ -2168,7 +2216,7 @@ function createSnippetPrinter( sourceFile: SourceFile | undefined, ): string { const unescaped = printUnescapedSnippetList(format, list, sourceFile); - return escapes ? textChanges.applyChanges(unescaped, escapes) : unescaped; + return escapes ? applyChanges(unescaped, escapes) : unescaped; } function printUnescapedSnippetList( @@ -2186,7 +2234,7 @@ function createSnippetPrinter( format: ListFormat, list: NodeArray, sourceFile: SourceFile, - formatContext: formatting.FormatContext, + formatContext: FormatContext, ): string { const syntheticFile = { text: printUnescapedSnippetList( @@ -2200,8 +2248,8 @@ function createSnippetPrinter( const formatOptions = getFormatCodeSettingsForWriting(formatContext, sourceFile); const changes = flatMap(list, node => { - const nodeWithPos = textChanges.assignPositionsToNode(node); - return formatting.formatNodeGivenIndentation( + const nodeWithPos = assignPositionsToNode(node); + return formatNodeGivenIndentation( nodeWithPos, syntheticFile, sourceFile.languageVariant, @@ -2213,13 +2261,13 @@ function createSnippetPrinter( const allChanges = escapes ? stableSort(concatenate(changes, escapes), (a, b) => compareTextSpans(a.span, b.span)) : changes; - return textChanges.applyChanges(syntheticFile.text, allChanges); + return applyChanges(syntheticFile.text, allChanges); } /** Snippet-escaping version of `printer.printNode`. */ function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string { const unescaped = printUnescapedNode(hint, node, sourceFile); - return escapes ? textChanges.applyChanges(unescaped, escapes) : unescaped; + return escapes ? applyChanges(unescaped, escapes) : unescaped; } function printUnescapedNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string { @@ -2233,7 +2281,7 @@ function createSnippetPrinter( hint: EmitHint, node: Node, sourceFile: SourceFile, - formatContext: formatting.FormatContext): string { + formatContext: FormatContext): string { const syntheticFile = { text: printUnescapedNode( hint, @@ -2245,8 +2293,8 @@ function createSnippetPrinter( }; const formatOptions = getFormatCodeSettingsForWriting(formatContext, sourceFile); - const nodeWithPos = textChanges.assignPositionsToNode(node); - const changes = formatting.formatNodeGivenIndentation( + const nodeWithPos = assignPositionsToNode(node); + const changes = formatNodeGivenIndentation( nodeWithPos, syntheticFile, sourceFile.languageVariant, @@ -2257,7 +2305,7 @@ function createSnippetPrinter( const allChanges = escapes ? stableSort(concatenate(changes, escapes), (a, b) => compareTextSpans(a.span, b.span)) : changes; - return textChanges.applyChanges(syntheticFile.text, allChanges); + return applyChanges(syntheticFile.text, allChanges); } } @@ -2323,7 +2371,7 @@ function getInsertTextAndReplacementSpanForImportCompletion(name: string, import origin.exportName === InternalSymbolName.ExportEquals ? ExportKind.ExportEquals : ExportKind.Named; const tabStop = preferences.includeCompletionsWithSnippetText ? "$1" : ""; - const importKind = codefix.getImportKind(sourceFile, exportKind, options, /*forceImportKeyword*/ true); + const importKind = getImportKind(sourceFile, exportKind, options, /*forceImportKeyword*/ true); const isImportSpecifierTypeOnly = importStatementCompletion.couldBeTypeOnlyImportSpecifier; const topLevelTypeOnlyText = importStatementCompletion.isTopLevelTypeOnly ? ` ${tokenToString(SyntaxKind.TypeKeyword)} ` : " "; const importSpecifierTypeOnlyText = isImportSpecifierTypeOnly ? `${tokenToString(SyntaxKind.TypeKeyword)} ` : ""; @@ -2380,7 +2428,7 @@ export function getCompletionEntriesFromSymbols( kind: CompletionKind, preferences: UserPreferences, compilerOptions: CompilerOptions, - formatContext: formatting.FormatContext | undefined, + formatContext: FormatContext | undefined, isTypeOnlyLocation?: boolean, propertyAccessToConvert?: PropertyAccessExpression, jsxIdentifierExpected?: boolean, @@ -2640,7 +2688,7 @@ export function getCompletionEntryDetails( position: number, entryId: CompletionEntryIdentifier, host: LanguageServiceHost, - formatContext: formatting.FormatContext, + formatContext: FormatContext, preferences: UserPreferences, cancellationToken: CancellationToken, ): CompletionEntryDetails | undefined { @@ -2660,11 +2708,11 @@ export function getCompletionEntryDetails( const { request } = symbolCompletion; switch (request.kind) { case CompletionDataKind.JsDocTagName: - return JsDoc.getJSDocTagNameCompletionDetails(name); + return getJSDocTagNameCompletionDetails(name); case CompletionDataKind.JsDocTag: - return JsDoc.getJSDocTagCompletionDetails(name); + return getJSDocTagCompletionDetails(name); case CompletionDataKind.JsDocParameterName: - return JsDoc.getJSDocParameterNameCompletionDetails(name); + return getJSDocParameterNameCompletionDetails(name); case CompletionDataKind.Keywords: return some(request.keywordCompletions, c => c.name === name) ? createSimpleDetails(name, ScriptElementKind.keyword, SymbolDisplayPartKind.keyword) : undefined; default: @@ -2691,9 +2739,7 @@ export function getCompletionEntryDetails( program, /*formatContext*/ undefined)!; if (importAdder.hasFixes()) { - const changes = textChanges.ChangeTracker.with( - { host, formatContext, preferences }, - importAdder.writeFixes); + const changes = ChangeTracker.with({ host, formatContext, preferences }, importAdder.writeFixes); return { name: entry.name, kind: ScriptElementKind.unknown, @@ -2730,9 +2776,9 @@ function createSimpleDetails(name: string, kind: ScriptElementKind, kind2: Symbo export function createCompletionDetailsForSymbol(symbol: Symbol, name: string, checker: TypeChecker, sourceFile: SourceFile, location: Node, cancellationToken: CancellationToken, codeActions?: CodeAction[], sourceDisplay?: SymbolDisplayPart[]): CompletionEntryDetails { const { displayParts, documentation, symbolKind, tags } = checker.runWithCancellationToken(cancellationToken, checker => - SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All) + getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, sourceFile, location, location, SemanticMeaning.All) ); - return createCompletionDetails(name, SymbolDisplay.getSymbolModifiers(checker, symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay); + return createCompletionDetails(name, getSymbolModifiers(checker, symbol), symbolKind, displayParts, documentation, tags, codeActions, sourceDisplay); } /** @internal */ @@ -2756,7 +2802,7 @@ function getCompletionEntryCodeActionsAndSourceDisplay( sourceFile: SourceFile, position: number, previousToken: Node | undefined, - formatContext: formatting.FormatContext, + formatContext: FormatContext, preferences: UserPreferences, data: CompletionEntryData | undefined, source: string | undefined, @@ -2782,7 +2828,7 @@ function getCompletionEntryCodeActionsAndSourceDisplay( contextToken, formatContext); if (importAdder) { - const changes = textChanges.ChangeTracker.with( + const changes = ChangeTracker.with( { host, formatContext, preferences }, importAdder.writeFixes); return { @@ -2796,7 +2842,7 @@ function getCompletionEntryCodeActionsAndSourceDisplay( } if (originIsTypeOnlyAlias(origin)) { - const codeAction = codefix.getPromoteTypeOnlyCompletionAction( + const codeAction = getPromoteTypeOnlyCompletionAction( sourceFile, origin.declaration.name, program, @@ -2816,7 +2862,7 @@ function getCompletionEntryCodeActionsAndSourceDisplay( const { moduleSymbol } = origin; const targetSymbol = checker.getMergedSymbol(skipAlias(symbol.exportSymbol || symbol, checker)); const isJsxOpeningTagName = contextToken?.kind === SyntaxKind.LessThanToken && isJsxOpeningLikeElement(contextToken.parent); - const { moduleSpecifier, codeAction } = codefix.getImportCompletionAction( + const { moduleSpecifier, codeAction } = getImportCompletionAction( targetSymbol, moduleSymbol, data?.exportMapKey, @@ -2931,7 +2977,7 @@ function getContextualType(previousToken: Node, position: number, sourceFile: So case SyntaxKind.OpenBraceToken: return isJsxExpression(parent) && !isJsxElement(parent.parent) && !isJsxFragment(parent.parent) ? checker.getContextualTypeForJsxAttribute(parent.parent) : undefined; default: - const argInfo = SignatureHelp.getArgumentInfoForCompletions(previousToken, position, sourceFile); + const argInfo = getArgumentInfoForCompletions(previousToken, position, sourceFile); return argInfo ? // At `,`, treat this as the next argument after the comma. checker.getContextualTypeForArgumentAtIndex(argInfo.invocation, argInfo.argumentIndex + (previousToken.kind === SyntaxKind.CommaToken ? 1 : 0)) : @@ -2961,7 +3007,7 @@ function getCompletionData( preferences: UserPreferences, detailsEntryId: CompletionEntryIdentifier | undefined, host: LanguageServiceHost, - formatContext: formatting.FormatContext | undefined, + formatContext: FormatContext | undefined, cancellationToken?: CancellationToken, ): CompletionData | Request | undefined { const typeChecker = program.getTypeChecker(); @@ -3229,7 +3275,7 @@ function getCompletionData( let hasUnresolvedAutoImports = false; // This also gets mutated in nested-functions after the return let symbols: Symbol[] = []; - let importSpecifierResolver: codefix.ImportSpecifierResolver | undefined; + let importSpecifierResolver: ImportSpecifierResolver | undefined; const symbolToOriginInfoMap: SymbolOriginInfoMap = []; const symbolToSortTextMap: SymbolSortTextMap = []; const seenPropertySymbols = new Map(); @@ -3489,7 +3535,7 @@ function getCompletionData( } else { const fileName = isExternalModuleNameRelative(stripQuotes(moduleSymbol.name)) ? getSourceFileOfModule(moduleSymbol)?.fileName : undefined; - const { moduleSpecifier } = (importSpecifierResolver ||= codefix.createImportSpecifierResolver(sourceFile, program, host, preferences)).getModuleSpecifierForBestExportInfo([{ + const { moduleSpecifier } = (importSpecifierResolver ||= createImportSpecifierResolver(sourceFile, program, host, preferences)).getModuleSpecifierForBestExportInfo([{ exportKind: ExportKind.Named, moduleFileName: fileName, isFromPackageJson: false, @@ -3776,7 +3822,7 @@ function getCompletionData( resolvingModuleSpecifiers( "collectAutoImports", host, - importSpecifierResolver ||= codefix.createImportSpecifierResolver(sourceFile, program, host, preferences), + importSpecifierResolver ||= createImportSpecifierResolver(sourceFile, program, host, preferences), program, position, preferences, diff --git a/src/services/documentHighlights.ts b/src/services/documentHighlights.ts index 1d4848c641851..0913c0562c870 100644 --- a/src/services/documentHighlights.ts +++ b/src/services/documentHighlights.ts @@ -1,72 +1,58 @@ import { - __String, arrayFrom, arrayToMultiMap, - Block, - BreakOrContinueStatement, - CancellationToken, - canHaveSymbol, - CaseClause, cast, concatenate, - ConstructorDeclaration, contains, createGetCanonicalFileName, - createTextSpanFromBounds, - createTextSpanFromNode, - Debug, - DefaultClause, find, - FindAllReferences, - findAncestor, - findChildOfKind, - findModifier, forEach, - forEachChild, - forEachReturnStatement, - FunctionDeclaration, - FunctionLikeDeclaration, - getContainingFunction, - getTouchingPropertyName, - HighlightSpan, - HighlightSpanKind, - IfStatement, - isAccessor, + isWhiteSpaceSingleLine, + mapDefined, + mapDefinedIterator, + toArray, + tryCast, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { isAwaitExpression, isBlock, - isBreakOrContinueStatement, isCaseClause, isClassDeclaration, - isClassLike, isConstructorDeclaration, - isDeclaration, isDefaultClause, - isFunctionBlock, - isFunctionLike, isIfStatement, isInterfaceDeclaration, - isIterationStatement, isJsxClosingElement, isJsxOpeningElement, isLabeledStatement, - isModifierKind, isModuleDeclaration, isReturnStatement, isSwitchStatement, isThrowStatement, isTryStatement, isTypeAliasDeclaration, - isTypeNode, isVariableStatement, - isWhiteSpaceSingleLine, isYieldExpression, +} from "../compiler/factory/nodeTests"; +import { forEachChild } from "../compiler/parser"; +import { forEachReturnStatement } from "../compiler/parserUtilities"; +import { toPath } from "../compiler/path"; +import { + __String, + Block, + BreakOrContinueStatement, + CancellationToken, + CaseClause, + ConstructorDeclaration, + DefaultClause, + FunctionDeclaration, + FunctionLikeDeclaration, + IfStatement, IterationStatement, - mapDefined, - mapDefinedIterator, MethodDeclaration, Modifier, ModifierFlags, - modifierToFlag, ModuleBlock, Node, ObjectLiteralExpression, @@ -77,528 +63,551 @@ import { SwitchStatement, SyntaxKind, ThrowStatement, - toArray, - toPath, - tryCast, TryStatement, -} from "./_namespaces/ts"; - -export interface DocumentHighlights { - fileName: string; - highlightSpans: HighlightSpan[]; -} +} from "../compiler/types"; +import { + getContainingFunction, + isFunctionBlock, + modifierToFlag, +} from "../compiler/utilities"; +import { + canHaveSymbol, + createTextSpanFromBounds, + findAncestor, + isAccessor, + isBreakOrContinueStatement, + isClassLike, + isDeclaration, + isFunctionLike, + isIterationStatement, + isModifierKind, + isTypeNode, +} from "../compiler/utilitiesPublic"; +import { + getReferenceEntriesForNode, + toHighlightSpan, +} from "./findAllReferences"; +import { + DocumentHighlights, + HighlightSpan, + HighlightSpanKind, +} from "./types"; +import { + createTextSpanFromNode, + findChildOfKind, + findModifier, + getTouchingPropertyName, +} from "./utilities"; /** @internal */ -export namespace DocumentHighlights { - export function getDocumentHighlights(program: Program, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: readonly SourceFile[]): DocumentHighlights[] | undefined { - const node = getTouchingPropertyName(sourceFile, position); - - if (node.parent && (isJsxOpeningElement(node.parent) && node.parent.tagName === node || isJsxClosingElement(node.parent))) { - // For a JSX element, just highlight the matching tag, not all references. - const { openingElement, closingElement } = node.parent.parent; - const highlightSpans = [openingElement, closingElement].map(({ tagName }) => getHighlightSpanForNode(tagName, sourceFile)); - return [{ fileName: sourceFile.fileName, highlightSpans }]; - } - - return getSemanticDocumentHighlights(position, node, program, cancellationToken, sourceFilesToSearch) || getSyntacticDocumentHighlights(node, sourceFile); +export function getDocumentHighlights(program: Program, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: readonly SourceFile[]): DocumentHighlights[] | undefined { + const node = getTouchingPropertyName(sourceFile, position); + + if (node.parent && (isJsxOpeningElement(node.parent) && node.parent.tagName === node || isJsxClosingElement(node.parent))) { + // For a JSX element, just highlight the matching tag, not all references. + const { openingElement, closingElement } = node.parent.parent; + const highlightSpans = [openingElement, closingElement].map(({ tagName }) => getHighlightSpanForNode(tagName, sourceFile)); + return [{ fileName: sourceFile.fileName, highlightSpans }]; } - function getHighlightSpanForNode(node: Node, sourceFile: SourceFile): HighlightSpan { - return { - fileName: sourceFile.fileName, - textSpan: createTextSpanFromNode(node, sourceFile), - kind: HighlightSpanKind.none - }; - } - - function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: readonly SourceFile[]): DocumentHighlights[] | undefined { - const sourceFilesSet = new Set(sourceFilesToSearch.map(f => f.fileName)); - const referenceEntries = FindAllReferences.getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken, /*options*/ undefined, sourceFilesSet); - if (!referenceEntries) return undefined; - const map = arrayToMultiMap(referenceEntries.map(FindAllReferences.toHighlightSpan), e => e.fileName, e => e.span); - const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames()); - return arrayFrom(mapDefinedIterator(map.entries(), ([fileName, highlightSpans]) => { - if (!sourceFilesSet.has(fileName)) { - if (!program.redirectTargetsMap.has(toPath(fileName, program.getCurrentDirectory(), getCanonicalFileName))) { - return undefined; - } - const redirectTarget = program.getSourceFile(fileName); - const redirect = find(sourceFilesToSearch, f => !!f.redirectInfo && f.redirectInfo.redirectTarget === redirectTarget)!; - fileName = redirect.fileName; - Debug.assert(sourceFilesSet.has(fileName)); - } - return { fileName, highlightSpans }; - })); - } + return getSemanticDocumentHighlights(position, node, program, cancellationToken, sourceFilesToSearch) || getSyntacticDocumentHighlights(node, sourceFile); +} - function getSyntacticDocumentHighlights(node: Node, sourceFile: SourceFile): DocumentHighlights[] | undefined { - const highlightSpans = getHighlightSpans(node, sourceFile); - return highlightSpans && [{ fileName: sourceFile.fileName, highlightSpans }]; - } +function getHighlightSpanForNode(node: Node, sourceFile: SourceFile): HighlightSpan { + return { + fileName: sourceFile.fileName, + textSpan: createTextSpanFromNode(node, sourceFile), + kind: HighlightSpanKind.none + }; +} - function getHighlightSpans(node: Node, sourceFile: SourceFile): HighlightSpan[] | undefined { - switch (node.kind) { - case SyntaxKind.IfKeyword: - case SyntaxKind.ElseKeyword: - return isIfStatement(node.parent) ? getIfElseOccurrences(node.parent, sourceFile) : undefined; - case SyntaxKind.ReturnKeyword: - return useParent(node.parent, isReturnStatement, getReturnOccurrences); - case SyntaxKind.ThrowKeyword: - return useParent(node.parent, isThrowStatement, getThrowOccurrences); - case SyntaxKind.TryKeyword: - case SyntaxKind.CatchKeyword: - case SyntaxKind.FinallyKeyword: - const tryStatement = node.kind === SyntaxKind.CatchKeyword ? node.parent.parent : node.parent; - return useParent(tryStatement, isTryStatement, getTryCatchFinallyOccurrences); - case SyntaxKind.SwitchKeyword: - return useParent(node.parent, isSwitchStatement, getSwitchCaseDefaultOccurrences); - case SyntaxKind.CaseKeyword: - case SyntaxKind.DefaultKeyword: { - if (isDefaultClause(node.parent) || isCaseClause(node.parent)) { - return useParent(node.parent.parent.parent, isSwitchStatement, getSwitchCaseDefaultOccurrences); - } +function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: readonly SourceFile[]): DocumentHighlights[] | undefined { + const sourceFilesSet = new Set(sourceFilesToSearch.map(f => f.fileName)); + const referenceEntries = getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken, /*options*/ undefined, sourceFilesSet); + if (!referenceEntries) return undefined; + const map = arrayToMultiMap(referenceEntries.map(toHighlightSpan), e => e.fileName, e => e.span); + const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames()); + return arrayFrom(mapDefinedIterator(map.entries(), ([fileName, highlightSpans]) => { + if (!sourceFilesSet.has(fileName)) { + if (!program.redirectTargetsMap.has(toPath(fileName, program.getCurrentDirectory(), getCanonicalFileName))) { return undefined; } - case SyntaxKind.BreakKeyword: - case SyntaxKind.ContinueKeyword: - return useParent(node.parent, isBreakOrContinueStatement, getBreakOrContinueStatementOccurrences); - case SyntaxKind.ForKeyword: - case SyntaxKind.WhileKeyword: - case SyntaxKind.DoKeyword: - return useParent(node.parent, (n): n is IterationStatement => isIterationStatement(n, /*lookInLabeledStatements*/ true), getLoopBreakContinueOccurrences); - case SyntaxKind.ConstructorKeyword: - return getFromAllDeclarations(isConstructorDeclaration, [SyntaxKind.ConstructorKeyword]); - case SyntaxKind.GetKeyword: - case SyntaxKind.SetKeyword: - return getFromAllDeclarations(isAccessor, [SyntaxKind.GetKeyword, SyntaxKind.SetKeyword]); - case SyntaxKind.AwaitKeyword: - return useParent(node.parent, isAwaitExpression, getAsyncAndAwaitOccurrences); - case SyntaxKind.AsyncKeyword: - return highlightSpans(getAsyncAndAwaitOccurrences(node)); - case SyntaxKind.YieldKeyword: - return highlightSpans(getYieldOccurrences(node)); - case SyntaxKind.InKeyword: - return undefined; - default: - return isModifierKind(node.kind) && (isDeclaration(node.parent) || isVariableStatement(node.parent)) - ? highlightSpans(getModifierOccurrences(node.kind, node.parent)) - : undefined; + const redirectTarget = program.getSourceFile(fileName); + const redirect = find(sourceFilesToSearch, f => !!f.redirectInfo && f.redirectInfo.redirectTarget === redirectTarget)!; + fileName = redirect.fileName; + Debug.assert(sourceFilesSet.has(fileName)); } + return { fileName, highlightSpans }; + })); +} - function getFromAllDeclarations(nodeTest: (node: Node) => node is T, keywords: readonly SyntaxKind[]): HighlightSpan[] | undefined { - return useParent(node.parent, nodeTest, decl => mapDefined(tryCast(decl, canHaveSymbol)?.symbol.declarations, d => - nodeTest(d) ? find(d.getChildren(sourceFile), c => contains(keywords, c.kind)) : undefined)); - } +function getSyntacticDocumentHighlights(node: Node, sourceFile: SourceFile): DocumentHighlights[] | undefined { + const highlightSpans = getHighlightSpans(node, sourceFile); + return highlightSpans && [{ fileName: sourceFile.fileName, highlightSpans }]; +} - function useParent(node: Node, nodeTest: (node: Node) => node is T, getNodes: (node: T, sourceFile: SourceFile) => readonly Node[] | undefined): HighlightSpan[] | undefined { - return nodeTest(node) ? highlightSpans(getNodes(node, sourceFile)) : undefined; +function getHighlightSpans(node: Node, sourceFile: SourceFile): HighlightSpan[] | undefined { + switch (node.kind) { + case SyntaxKind.IfKeyword: + case SyntaxKind.ElseKeyword: + return isIfStatement(node.parent) ? getIfElseOccurrences(node.parent, sourceFile) : undefined; + case SyntaxKind.ReturnKeyword: + return useParent(node.parent, isReturnStatement, getReturnOccurrences); + case SyntaxKind.ThrowKeyword: + return useParent(node.parent, isThrowStatement, getThrowOccurrences); + case SyntaxKind.TryKeyword: + case SyntaxKind.CatchKeyword: + case SyntaxKind.FinallyKeyword: + const tryStatement = node.kind === SyntaxKind.CatchKeyword ? node.parent.parent : node.parent; + return useParent(tryStatement, isTryStatement, getTryCatchFinallyOccurrences); + case SyntaxKind.SwitchKeyword: + return useParent(node.parent, isSwitchStatement, getSwitchCaseDefaultOccurrences); + case SyntaxKind.CaseKeyword: + case SyntaxKind.DefaultKeyword: { + if (isDefaultClause(node.parent) || isCaseClause(node.parent)) { + return useParent(node.parent.parent.parent, isSwitchStatement, getSwitchCaseDefaultOccurrences); + } + return undefined; } + case SyntaxKind.BreakKeyword: + case SyntaxKind.ContinueKeyword: + return useParent(node.parent, isBreakOrContinueStatement, getBreakOrContinueStatementOccurrences); + case SyntaxKind.ForKeyword: + case SyntaxKind.WhileKeyword: + case SyntaxKind.DoKeyword: + return useParent(node.parent, (n): n is IterationStatement => isIterationStatement(n, /*lookInLabeledStatements*/ true), getLoopBreakContinueOccurrences); + case SyntaxKind.ConstructorKeyword: + return getFromAllDeclarations(isConstructorDeclaration, [SyntaxKind.ConstructorKeyword]); + case SyntaxKind.GetKeyword: + case SyntaxKind.SetKeyword: + return getFromAllDeclarations(isAccessor, [SyntaxKind.GetKeyword, SyntaxKind.SetKeyword]); + case SyntaxKind.AwaitKeyword: + return useParent(node.parent, isAwaitExpression, getAsyncAndAwaitOccurrences); + case SyntaxKind.AsyncKeyword: + return highlightSpans(getAsyncAndAwaitOccurrences(node)); + case SyntaxKind.YieldKeyword: + return highlightSpans(getYieldOccurrences(node)); + case SyntaxKind.InKeyword: + return undefined; + default: + return isModifierKind(node.kind) && (isDeclaration(node.parent) || isVariableStatement(node.parent)) + ? highlightSpans(getModifierOccurrences(node.kind, node.parent)) + : undefined; + } - function highlightSpans(nodes: readonly Node[] | undefined): HighlightSpan[] | undefined { - return nodes && nodes.map(node => getHighlightSpanForNode(node, sourceFile)); - } + function getFromAllDeclarations(nodeTest: (node: Node) => node is T, keywords: readonly SyntaxKind[]): HighlightSpan[] | undefined { + return useParent(node.parent, nodeTest, decl => mapDefined(tryCast(decl, canHaveSymbol)?.symbol.declarations, d => + nodeTest(d) ? find(d.getChildren(sourceFile), c => contains(keywords, c.kind)) : undefined)); } - /** - * Aggregates all throw-statements within this node *without* crossing - * into function boundaries and try-blocks with catch-clauses. - */ - function aggregateOwnedThrowStatements(node: Node): readonly ThrowStatement[] | undefined { - if (isThrowStatement(node)) { - return [node]; - } - else if (isTryStatement(node)) { - // Exceptions thrown within a try block lacking a catch clause are "owned" in the current context. - return concatenate( - node.catchClause ? aggregateOwnedThrowStatements(node.catchClause) : node.tryBlock && aggregateOwnedThrowStatements(node.tryBlock), - node.finallyBlock && aggregateOwnedThrowStatements(node.finallyBlock)); - } - // Do not cross function boundaries. - return isFunctionLike(node) ? undefined : flatMapChildren(node, aggregateOwnedThrowStatements); + function useParent(node: Node, nodeTest: (node: Node) => node is T, getNodes: (node: T, sourceFile: SourceFile) => readonly Node[] | undefined): HighlightSpan[] | undefined { + return nodeTest(node) ? highlightSpans(getNodes(node, sourceFile)) : undefined; } - /** - * For lack of a better name, this function takes a throw statement and returns the - * nearest ancestor that is a try-block (whose try statement has a catch clause), - * function-block, or source file. - */ - function getThrowStatementOwner(throwStatement: ThrowStatement): Node | undefined { - let child: Node = throwStatement; + function highlightSpans(nodes: readonly Node[] | undefined): HighlightSpan[] | undefined { + return nodes && nodes.map(node => getHighlightSpanForNode(node, sourceFile)); + } +} - while (child.parent) { - const parent = child.parent; +/** + * Aggregates all throw-statements within this node *without* crossing + * into function boundaries and try-blocks with catch-clauses. + */ +function aggregateOwnedThrowStatements(node: Node): readonly ThrowStatement[] | undefined { + if (isThrowStatement(node)) { + return [node]; + } + else if (isTryStatement(node)) { + // Exceptions thrown within a try block lacking a catch clause are "owned" in the current context. + return concatenate( + node.catchClause ? aggregateOwnedThrowStatements(node.catchClause) : node.tryBlock && aggregateOwnedThrowStatements(node.tryBlock), + node.finallyBlock && aggregateOwnedThrowStatements(node.finallyBlock)); + } + // Do not cross function boundaries. + return isFunctionLike(node) ? undefined : flatMapChildren(node, aggregateOwnedThrowStatements); +} - if (isFunctionBlock(parent) || parent.kind === SyntaxKind.SourceFile) { - return parent; - } +/** + * For lack of a better name, this function takes a throw statement and returns the + * nearest ancestor that is a try-block (whose try statement has a catch clause), + * function-block, or source file. + */ +function getThrowStatementOwner(throwStatement: ThrowStatement): Node | undefined { + let child: Node = throwStatement; - // A throw-statement is only owned by a try-statement if the try-statement has - // a catch clause, and if the throw-statement occurs within the try block. - if (isTryStatement(parent) && parent.tryBlock === child && parent.catchClause) { - return child; - } + while (child.parent) { + const parent = child.parent; - child = parent; + if (isFunctionBlock(parent) || parent.kind === SyntaxKind.SourceFile) { + return parent; } - return undefined; - } + // A throw-statement is only owned by a try-statement if the try-statement has + // a catch clause, and if the throw-statement occurs within the try block. + if (isTryStatement(parent) && parent.tryBlock === child && parent.catchClause) { + return child; + } - function aggregateAllBreakAndContinueStatements(node: Node): readonly BreakOrContinueStatement[] | undefined { - return isBreakOrContinueStatement(node) ? [node] : isFunctionLike(node) ? undefined : flatMapChildren(node, aggregateAllBreakAndContinueStatements); + child = parent; } - function flatMapChildren(node: Node, cb: (child: Node) => readonly T[] | T | undefined): readonly T[] { - const result: T[] = []; - node.forEachChild(child => { - const value = cb(child); - if (value !== undefined) { - result.push(...toArray(value)); - } - }); - return result; - } + return undefined; +} - function ownsBreakOrContinueStatement(owner: Node, statement: BreakOrContinueStatement): boolean { - const actualOwner = getBreakOrContinueOwner(statement); - return !!actualOwner && actualOwner === owner; - } +function aggregateAllBreakAndContinueStatements(node: Node): readonly BreakOrContinueStatement[] | undefined { + return isBreakOrContinueStatement(node) ? [node] : isFunctionLike(node) ? undefined : flatMapChildren(node, aggregateAllBreakAndContinueStatements); +} - function getBreakOrContinueOwner(statement: BreakOrContinueStatement): Node | undefined { - return findAncestor(statement, node => { - switch (node.kind) { - case SyntaxKind.SwitchStatement: - if (statement.kind === SyntaxKind.ContinueStatement) { - return false; - } - // falls through - - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.WhileStatement: - case SyntaxKind.DoStatement: - return !statement.label || isLabeledBy(node, statement.label.escapedText); - default: - // Don't cross function boundaries. - // TODO: GH#20090 - return isFunctionLike(node) && "quit"; - } - }); - } +function flatMapChildren(node: Node, cb: (child: Node) => readonly T[] | T | undefined): readonly T[] { + const result: T[] = []; + node.forEachChild(child => { + const value = cb(child); + if (value !== undefined) { + result.push(...toArray(value)); + } + }); + return result; +} - function getModifierOccurrences(modifier: Modifier["kind"], declaration: Node): Node[] { - return mapDefined(getNodesToSearchForModifier(declaration, modifierToFlag(modifier)), node => findModifier(node, modifier)); - } +function ownsBreakOrContinueStatement(owner: Node, statement: BreakOrContinueStatement): boolean { + const actualOwner = getBreakOrContinueOwner(statement); + return !!actualOwner && actualOwner === owner; +} - function getNodesToSearchForModifier(declaration: Node, modifierFlag: ModifierFlags): readonly Node[] | undefined { - // Types of node whose children might have modifiers. - const container = declaration.parent as ModuleBlock | SourceFile | Block | CaseClause | DefaultClause | ConstructorDeclaration | MethodDeclaration | FunctionDeclaration | ObjectTypeDeclaration | ObjectLiteralExpression; - switch (container.kind) { - case SyntaxKind.ModuleBlock: - case SyntaxKind.SourceFile: - case SyntaxKind.Block: - case SyntaxKind.CaseClause: - case SyntaxKind.DefaultClause: - // Container is either a class declaration or the declaration is a classDeclaration - if (modifierFlag & ModifierFlags.Abstract && isClassDeclaration(declaration)) { - return [...declaration.members, declaration]; - } - else { - return container.statements; - } - case SyntaxKind.Constructor: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.FunctionDeclaration: - return [...container.parameters, ...(isClassLike(container.parent) ? container.parent.members : [])]; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeLiteral: - const nodes = container.members; - - // If we're an accessibility modifier, we're in an instance member and should search - // the constructor's parameter list for instance members as well. - if (modifierFlag & (ModifierFlags.AccessibilityModifier | ModifierFlags.Readonly)) { - const constructor = find(container.members, isConstructorDeclaration); - if (constructor) { - return [...nodes, ...constructor.parameters]; - } +function getBreakOrContinueOwner(statement: BreakOrContinueStatement): Node | undefined { + return findAncestor(statement, node => { + switch (node.kind) { + case SyntaxKind.SwitchStatement: + if (statement.kind === SyntaxKind.ContinueStatement) { + return false; } - else if (modifierFlag & ModifierFlags.Abstract) { - return [...nodes, container]; + // falls through + + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.DoStatement: + return !statement.label || isLabeledBy(node, statement.label.escapedText); + default: + // Don't cross function boundaries. + // TODO: GH#20090 + return isFunctionLike(node) && "quit"; + } + }); +} + +function getModifierOccurrences(modifier: Modifier["kind"], declaration: Node): Node[] { + return mapDefined(getNodesToSearchForModifier(declaration, modifierToFlag(modifier)), node => findModifier(node, modifier)); +} + +function getNodesToSearchForModifier(declaration: Node, modifierFlag: ModifierFlags): readonly Node[] | undefined { + // Types of node whose children might have modifiers. + const container = declaration.parent as ModuleBlock | SourceFile | Block | CaseClause | DefaultClause | ConstructorDeclaration | MethodDeclaration | FunctionDeclaration | ObjectTypeDeclaration | ObjectLiteralExpression; + switch (container.kind) { + case SyntaxKind.ModuleBlock: + case SyntaxKind.SourceFile: + case SyntaxKind.Block: + case SyntaxKind.CaseClause: + case SyntaxKind.DefaultClause: + // Container is either a class declaration or the declaration is a classDeclaration + if (modifierFlag & ModifierFlags.Abstract && isClassDeclaration(declaration)) { + return [...declaration.members, declaration]; + } + else { + return container.statements; + } + case SyntaxKind.Constructor: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.FunctionDeclaration: + return [...container.parameters, ...(isClassLike(container.parent) ? container.parent.members : [])]; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeLiteral: + const nodes = container.members; + + // If we're an accessibility modifier, we're in an instance member and should search + // the constructor's parameter list for instance members as well. + if (modifierFlag & (ModifierFlags.AccessibilityModifier | ModifierFlags.Readonly)) { + const constructor = find(container.members, isConstructorDeclaration); + if (constructor) { + return [...nodes, ...constructor.parameters]; } - return nodes; + } + else if (modifierFlag & ModifierFlags.Abstract) { + return [...nodes, container]; + } + return nodes; - // Syntactically invalid positions that the parser might produce anyway - case SyntaxKind.ObjectLiteralExpression: - return undefined; + // Syntactically invalid positions that the parser might produce anyway + case SyntaxKind.ObjectLiteralExpression: + return undefined; - default: - Debug.assertNever(container, "Invalid container kind."); - } + default: + Debug.assertNever(container, "Invalid container kind."); } +} - function pushKeywordIf(keywordList: Node[], token: Node | undefined, ...expected: SyntaxKind[]): boolean { - if (token && contains(expected, token.kind)) { - keywordList.push(token); - return true; - } - - return false; +function pushKeywordIf(keywordList: Node[], token: Node | undefined, ...expected: SyntaxKind[]): boolean { + if (token && contains(expected, token.kind)) { + keywordList.push(token); + return true; } - function getLoopBreakContinueOccurrences(loopNode: IterationStatement): Node[] { - const keywords: Node[] = []; + return false; +} + +function getLoopBreakContinueOccurrences(loopNode: IterationStatement): Node[] { + const keywords: Node[] = []; - if (pushKeywordIf(keywords, loopNode.getFirstToken(), SyntaxKind.ForKeyword, SyntaxKind.WhileKeyword, SyntaxKind.DoKeyword)) { - // If we succeeded and got a do-while loop, then start looking for a 'while' keyword. - if (loopNode.kind === SyntaxKind.DoStatement) { - const loopTokens = loopNode.getChildren(); + if (pushKeywordIf(keywords, loopNode.getFirstToken(), SyntaxKind.ForKeyword, SyntaxKind.WhileKeyword, SyntaxKind.DoKeyword)) { + // If we succeeded and got a do-while loop, then start looking for a 'while' keyword. + if (loopNode.kind === SyntaxKind.DoStatement) { + const loopTokens = loopNode.getChildren(); - for (let i = loopTokens.length - 1; i >= 0; i--) { - if (pushKeywordIf(keywords, loopTokens[i], SyntaxKind.WhileKeyword)) { - break; - } + for (let i = loopTokens.length - 1; i >= 0; i--) { + if (pushKeywordIf(keywords, loopTokens[i], SyntaxKind.WhileKeyword)) { + break; } } } + } - forEach(aggregateAllBreakAndContinueStatements(loopNode.statement), statement => { - if (ownsBreakOrContinueStatement(loopNode, statement)) { - pushKeywordIf(keywords, statement.getFirstToken(), SyntaxKind.BreakKeyword, SyntaxKind.ContinueKeyword); - } - }); + forEach(aggregateAllBreakAndContinueStatements(loopNode.statement), statement => { + if (ownsBreakOrContinueStatement(loopNode, statement)) { + pushKeywordIf(keywords, statement.getFirstToken(), SyntaxKind.BreakKeyword, SyntaxKind.ContinueKeyword); + } + }); - return keywords; - } + return keywords; +} - function getBreakOrContinueStatementOccurrences(breakOrContinueStatement: BreakOrContinueStatement): Node[] | undefined { - const owner = getBreakOrContinueOwner(breakOrContinueStatement); +function getBreakOrContinueStatementOccurrences(breakOrContinueStatement: BreakOrContinueStatement): Node[] | undefined { + const owner = getBreakOrContinueOwner(breakOrContinueStatement); - if (owner) { - switch (owner.kind) { - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - return getLoopBreakContinueOccurrences(owner as IterationStatement); - case SyntaxKind.SwitchStatement: - return getSwitchCaseDefaultOccurrences(owner as SwitchStatement); + if (owner) { + switch (owner.kind) { + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + return getLoopBreakContinueOccurrences(owner as IterationStatement); + case SyntaxKind.SwitchStatement: + return getSwitchCaseDefaultOccurrences(owner as SwitchStatement); - } } - - return undefined; } - function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement): Node[] { - const keywords: Node[] = []; + return undefined; +} + +function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement): Node[] { + const keywords: Node[] = []; - pushKeywordIf(keywords, switchStatement.getFirstToken(), SyntaxKind.SwitchKeyword); + pushKeywordIf(keywords, switchStatement.getFirstToken(), SyntaxKind.SwitchKeyword); - // Go through each clause in the switch statement, collecting the 'case'/'default' keywords. - forEach(switchStatement.caseBlock.clauses, clause => { - pushKeywordIf(keywords, clause.getFirstToken(), SyntaxKind.CaseKeyword, SyntaxKind.DefaultKeyword); + // Go through each clause in the switch statement, collecting the 'case'/'default' keywords. + forEach(switchStatement.caseBlock.clauses, clause => { + pushKeywordIf(keywords, clause.getFirstToken(), SyntaxKind.CaseKeyword, SyntaxKind.DefaultKeyword); - forEach(aggregateAllBreakAndContinueStatements(clause), statement => { - if (ownsBreakOrContinueStatement(switchStatement, statement)) { - pushKeywordIf(keywords, statement.getFirstToken(), SyntaxKind.BreakKeyword); - } - }); + forEach(aggregateAllBreakAndContinueStatements(clause), statement => { + if (ownsBreakOrContinueStatement(switchStatement, statement)) { + pushKeywordIf(keywords, statement.getFirstToken(), SyntaxKind.BreakKeyword); + } }); + }); - return keywords; - } - - function getTryCatchFinallyOccurrences(tryStatement: TryStatement, sourceFile: SourceFile): Node[] { - const keywords: Node[] = []; + return keywords; +} - pushKeywordIf(keywords, tryStatement.getFirstToken(), SyntaxKind.TryKeyword); +function getTryCatchFinallyOccurrences(tryStatement: TryStatement, sourceFile: SourceFile): Node[] { + const keywords: Node[] = []; - if (tryStatement.catchClause) { - pushKeywordIf(keywords, tryStatement.catchClause.getFirstToken(), SyntaxKind.CatchKeyword); - } + pushKeywordIf(keywords, tryStatement.getFirstToken(), SyntaxKind.TryKeyword); - if (tryStatement.finallyBlock) { - const finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile)!; - pushKeywordIf(keywords, finallyKeyword, SyntaxKind.FinallyKeyword); - } + if (tryStatement.catchClause) { + pushKeywordIf(keywords, tryStatement.catchClause.getFirstToken(), SyntaxKind.CatchKeyword); + } - return keywords; + if (tryStatement.finallyBlock) { + const finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile)!; + pushKeywordIf(keywords, finallyKeyword, SyntaxKind.FinallyKeyword); } - function getThrowOccurrences(throwStatement: ThrowStatement, sourceFile: SourceFile): Node[] | undefined { - const owner = getThrowStatementOwner(throwStatement); + return keywords; +} - if (!owner) { - return undefined; - } +function getThrowOccurrences(throwStatement: ThrowStatement, sourceFile: SourceFile): Node[] | undefined { + const owner = getThrowStatementOwner(throwStatement); + + if (!owner) { + return undefined; + } + + const keywords: Node[] = []; - const keywords: Node[] = []; + forEach(aggregateOwnedThrowStatements(owner), throwStatement => { + keywords.push(findChildOfKind(throwStatement, SyntaxKind.ThrowKeyword, sourceFile)!); + }); - forEach(aggregateOwnedThrowStatements(owner), throwStatement => { - keywords.push(findChildOfKind(throwStatement, SyntaxKind.ThrowKeyword, sourceFile)!); + // If the "owner" is a function, then we equate 'return' and 'throw' statements in their + // ability to "jump out" of the function, and include occurrences for both. + if (isFunctionBlock(owner)) { + forEachReturnStatement(owner as Block, returnStatement => { + keywords.push(findChildOfKind(returnStatement, SyntaxKind.ReturnKeyword, sourceFile)!); }); + } - // If the "owner" is a function, then we equate 'return' and 'throw' statements in their - // ability to "jump out" of the function, and include occurrences for both. - if (isFunctionBlock(owner)) { - forEachReturnStatement(owner as Block, returnStatement => { - keywords.push(findChildOfKind(returnStatement, SyntaxKind.ReturnKeyword, sourceFile)!); - }); - } + return keywords; +} - return keywords; +function getReturnOccurrences(returnStatement: ReturnStatement, sourceFile: SourceFile): Node[] | undefined { + const func = getContainingFunction(returnStatement) as FunctionLikeDeclaration; + if (!func) { + return undefined; } - function getReturnOccurrences(returnStatement: ReturnStatement, sourceFile: SourceFile): Node[] | undefined { - const func = getContainingFunction(returnStatement) as FunctionLikeDeclaration; - if (!func) { - return undefined; - } + const keywords: Node[] = []; + forEachReturnStatement(cast(func.body, isBlock), returnStatement => { + keywords.push(findChildOfKind(returnStatement, SyntaxKind.ReturnKeyword, sourceFile)!); + }); - const keywords: Node[] = []; - forEachReturnStatement(cast(func.body, isBlock), returnStatement => { - keywords.push(findChildOfKind(returnStatement, SyntaxKind.ReturnKeyword, sourceFile)!); - }); + // Include 'throw' statements that do not occur within a try block. + forEach(aggregateOwnedThrowStatements(func.body!), throwStatement => { + keywords.push(findChildOfKind(throwStatement, SyntaxKind.ThrowKeyword, sourceFile)!); + }); - // Include 'throw' statements that do not occur within a try block. - forEach(aggregateOwnedThrowStatements(func.body!), throwStatement => { - keywords.push(findChildOfKind(throwStatement, SyntaxKind.ThrowKeyword, sourceFile)!); - }); + return keywords; +} - return keywords; +function getAsyncAndAwaitOccurrences(node: Node): Node[] | undefined { + const func = getContainingFunction(node) as FunctionLikeDeclaration; + if (!func) { + return undefined; } - function getAsyncAndAwaitOccurrences(node: Node): Node[] | undefined { - const func = getContainingFunction(node) as FunctionLikeDeclaration; - if (!func) { - return undefined; - } + const keywords: Node[] = []; - const keywords: Node[] = []; - - if (func.modifiers) { - func.modifiers.forEach(modifier => { - pushKeywordIf(keywords, modifier, SyntaxKind.AsyncKeyword); - }); - } + if (func.modifiers) { + func.modifiers.forEach(modifier => { + pushKeywordIf(keywords, modifier, SyntaxKind.AsyncKeyword); + }); + } - forEachChild(func, child => { - traverseWithoutCrossingFunction(child, node => { - if (isAwaitExpression(node)) { - pushKeywordIf(keywords, node.getFirstToken(), SyntaxKind.AwaitKeyword); - } - }); + forEachChild(func, child => { + traverseWithoutCrossingFunction(child, node => { + if (isAwaitExpression(node)) { + pushKeywordIf(keywords, node.getFirstToken(), SyntaxKind.AwaitKeyword); + } }); + }); - return keywords; - } + return keywords; +} - function getYieldOccurrences(node: Node): Node[] | undefined { - const func = getContainingFunction(node) as FunctionDeclaration; - if (!func) { - return undefined; - } +function getYieldOccurrences(node: Node): Node[] | undefined { + const func = getContainingFunction(node) as FunctionDeclaration; + if (!func) { + return undefined; + } - const keywords: Node[] = []; + const keywords: Node[] = []; - forEachChild(func, child => { - traverseWithoutCrossingFunction(child, node => { - if (isYieldExpression(node)) { - pushKeywordIf(keywords, node.getFirstToken(), SyntaxKind.YieldKeyword); - } - }); + forEachChild(func, child => { + traverseWithoutCrossingFunction(child, node => { + if (isYieldExpression(node)) { + pushKeywordIf(keywords, node.getFirstToken(), SyntaxKind.YieldKeyword); + } }); + }); - return keywords; - } + return keywords; +} - // Do not cross function/class/interface/module/type boundaries. - function traverseWithoutCrossingFunction(node: Node, cb: (node: Node) => void) { - cb(node); - if (!isFunctionLike(node) && !isClassLike(node) && !isInterfaceDeclaration(node) && !isModuleDeclaration(node) && !isTypeAliasDeclaration(node) && !isTypeNode(node)) { - forEachChild(node, child => traverseWithoutCrossingFunction(child, cb)); - } +// Do not cross function/class/interface/module/type boundaries. +function traverseWithoutCrossingFunction(node: Node, cb: (node: Node) => void) { + cb(node); + if (!isFunctionLike(node) && !isClassLike(node) && !isInterfaceDeclaration(node) && !isModuleDeclaration(node) && !isTypeAliasDeclaration(node) && !isTypeNode(node)) { + forEachChild(node, child => traverseWithoutCrossingFunction(child, cb)); } +} - function getIfElseOccurrences(ifStatement: IfStatement, sourceFile: SourceFile): HighlightSpan[] { - const keywords = getIfElseKeywords(ifStatement, sourceFile); - const result: HighlightSpan[] = []; - - // We'd like to highlight else/ifs together if they are only separated by whitespace - // (i.e. the keywords are separated by no comments, no newlines). - for (let i = 0; i < keywords.length; i++) { - if (keywords[i].kind === SyntaxKind.ElseKeyword && i < keywords.length - 1) { - const elseKeyword = keywords[i]; - const ifKeyword = keywords[i + 1]; // this *should* always be an 'if' keyword. - - let shouldCombineElseAndIf = true; - - // Avoid recalculating getStart() by iterating backwards. - for (let j = ifKeyword.getStart(sourceFile) - 1; j >= elseKeyword.end; j--) { - if (!isWhiteSpaceSingleLine(sourceFile.text.charCodeAt(j))) { - shouldCombineElseAndIf = false; - break; - } - } +function getIfElseOccurrences(ifStatement: IfStatement, sourceFile: SourceFile): HighlightSpan[] { + const keywords = getIfElseKeywords(ifStatement, sourceFile); + const result: HighlightSpan[] = []; - if (shouldCombineElseAndIf) { - result.push({ - fileName: sourceFile.fileName, - textSpan: createTextSpanFromBounds(elseKeyword.getStart(), ifKeyword.end), - kind: HighlightSpanKind.reference - }); - i++; // skip the next keyword - continue; + // We'd like to highlight else/ifs together if they are only separated by whitespace + // (i.e. the keywords are separated by no comments, no newlines). + for (let i = 0; i < keywords.length; i++) { + if (keywords[i].kind === SyntaxKind.ElseKeyword && i < keywords.length - 1) { + const elseKeyword = keywords[i]; + const ifKeyword = keywords[i + 1]; // this *should* always be an 'if' keyword. + + let shouldCombineElseAndIf = true; + + // Avoid recalculating getStart() by iterating backwards. + for (let j = ifKeyword.getStart(sourceFile) - 1; j >= elseKeyword.end; j--) { + if (!isWhiteSpaceSingleLine(sourceFile.text.charCodeAt(j))) { + shouldCombineElseAndIf = false; + break; } } - // Ordinary case: just highlight the keyword. - result.push(getHighlightSpanForNode(keywords[i], sourceFile)); + if (shouldCombineElseAndIf) { + result.push({ + fileName: sourceFile.fileName, + textSpan: createTextSpanFromBounds(elseKeyword.getStart(), ifKeyword.end), + kind: HighlightSpanKind.reference + }); + i++; // skip the next keyword + continue; + } } - return result; + // Ordinary case: just highlight the keyword. + result.push(getHighlightSpanForNode(keywords[i], sourceFile)); } - function getIfElseKeywords(ifStatement: IfStatement, sourceFile: SourceFile): Node[] { - const keywords: Node[] = []; + return result; +} - // Traverse upwards through all parent if-statements linked by their else-branches. - while (isIfStatement(ifStatement.parent) && ifStatement.parent.elseStatement === ifStatement) { - ifStatement = ifStatement.parent; - } +function getIfElseKeywords(ifStatement: IfStatement, sourceFile: SourceFile): Node[] { + const keywords: Node[] = []; - // Now traverse back down through the else branches, aggregating if/else keywords of if-statements. - while (true) { - const children = ifStatement.getChildren(sourceFile); - pushKeywordIf(keywords, children[0], SyntaxKind.IfKeyword); + // Traverse upwards through all parent if-statements linked by their else-branches. + while (isIfStatement(ifStatement.parent) && ifStatement.parent.elseStatement === ifStatement) { + ifStatement = ifStatement.parent; + } - // Generally the 'else' keyword is second-to-last, so we traverse backwards. - for (let i = children.length - 1; i >= 0; i--) { - if (pushKeywordIf(keywords, children[i], SyntaxKind.ElseKeyword)) { - break; - } - } + // Now traverse back down through the else branches, aggregating if/else keywords of if-statements. + while (true) { + const children = ifStatement.getChildren(sourceFile); + pushKeywordIf(keywords, children[0], SyntaxKind.IfKeyword); - if (!ifStatement.elseStatement || !isIfStatement(ifStatement.elseStatement)) { + // Generally the 'else' keyword is second-to-last, so we traverse backwards. + for (let i = children.length - 1; i >= 0; i--) { + if (pushKeywordIf(keywords, children[i], SyntaxKind.ElseKeyword)) { break; } + } - ifStatement = ifStatement.elseStatement; + if (!ifStatement.elseStatement || !isIfStatement(ifStatement.elseStatement)) { + break; } - return keywords; + ifStatement = ifStatement.elseStatement; } - /** - * Whether or not a 'node' is preceded by a label of the given string. - * Note: 'node' cannot be a SourceFile. - */ - function isLabeledBy(node: Node, labelName: __String): boolean { - return !!findAncestor(node.parent, owner => !isLabeledStatement(owner) ? "quit" : owner.label.escapedText === labelName); - } + return keywords; +} + +/** + * Whether or not a 'node' is preceded by a label of the given string. + * Note: 'node' cannot be a SourceFile. + */ +function isLabeledBy(node: Node, labelName: __String): boolean { + return !!findAncestor(node.parent, owner => !isLabeledStatement(owner) ? "quit" : owner.label.escapedText === labelName); } diff --git a/src/services/documentRegistry.ts b/src/services/documentRegistry.ts index 50f660d6bf746..aa64d47bd4b4f 100644 --- a/src/services/documentRegistry.ts +++ b/src/services/documentRegistry.ts @@ -1,32 +1,44 @@ +import { + sourceFileAffectingCompilerOptions, +} from "../compiler/commandLineParser"; import { arrayFrom, - CompilerOptions, createGetCanonicalFileName, - createLanguageServiceSourceFile, - CreateSourceFileOptions, - Debug, - ensureScriptKind, firstDefinedIterator, - forEachEntry, - getEmitScriptTarget, - getImpliedNodeFormatForFile, - getKeyForCompilerOptions, getOrUpdate, - getSetExternalModuleIndicator, identity, - IScriptSnapshot, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { getKeyForCompilerOptions } from "../compiler/moduleNameResolver"; +import { + CreateSourceFileOptions, isDeclarationFileName, +} from "../compiler/parser"; +import { toPath } from "../compiler/path"; +import { + getImpliedNodeFormatForFile, + getSetExternalModuleIndicator, +} from "../compiler/program"; +import { tracing } from "../compiler/tracing"; +import { + CompilerOptions, MinimalResolutionCacheHost, Path, ResolutionMode, ScriptKind, ScriptTarget, SourceFile, - sourceFileAffectingCompilerOptions, - toPath, - tracing, +} from "../compiler/types"; +import { + ensureScriptKind, + forEachEntry, + getEmitScriptTarget, +} from "../compiler/utilities"; +import { + createLanguageServiceSourceFile, updateLanguageServiceSourceFile, -} from "./_namespaces/ts"; +} from "./services"; +import { IScriptSnapshot } from "./types"; /** * The document registry represents a store of SourceFile objects that can be shared between diff --git a/src/services/exportInfoMap.ts b/src/services/exportInfoMap.ts index 4651c1dac1c36..711089a49fdf8 100644 --- a/src/services/exportInfoMap.ts +++ b/src/services/exportInfoMap.ts @@ -1,65 +1,86 @@ import { - __String, - addToSeen, arrayIsEqualTo, - CancellationToken, - CompilerOptions, - consumesNodeCoreModules, createMultiMap, - Debug, emptyArray, findIndex, firstDefined, + GetCanonicalFileName, + mapDefined, + startsWith, + stringContains, + tryCast, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { + isExportAssignment, + isExportSpecifier, + isIdentifier, +} from "../compiler/factory/nodeTests"; +import { + getPatternFromSpec, + getRegexFromPattern, +} from "../compiler/fileMatcher"; +import { + getPackageNameFromTypesPackageName, + nodeModulesPathPart, + unmangleScopedPackageName, +} from "../compiler/moduleNameResolver"; +import { forEachFileNameOfModule } from "../compiler/moduleSpecifiers"; +import { getNodeModulePathParts } from "../compiler/moduleSpecifiersUtilities"; +import { forEachAncestorDirectory, - forEachEntry, getBaseFileName, - GetCanonicalFileName, getDirectoryPath, +} from "../compiler/path"; +import { timestamp } from "../compiler/performanceCore"; +import { + __String, + CancellationToken, + CompilerOptions, + InternalSymbolName, + ModuleSpecifierCache, + ModuleSpecifierResolutionHost, + Path, + Program, + SourceFile, + Statement, + Symbol, + SymbolFlags, + TypeChecker, + UserPreferences, +} from "../compiler/types"; +import { + addToSeen, + forEachEntry, getLocalSymbolForExportDefault, - getNameForExportedSymbol, - getNamesForExportedSymbol, - getNodeModulePathParts, - getPackageNameFromTypesPackageName, - getPatternFromSpec, - getRegexFromPattern, getSymbolId, hostGetCanonicalFileName, hostUsesCaseSensitiveFileNames, - InternalSymbolName, - isExportAssignment, - isExportSpecifier, - isExternalModuleNameRelative, - isExternalModuleSymbol, isExternalOrCommonJsModule, - isIdentifier, isKnownSymbol, isNonGlobalAmbientModule, isPrivateIdentifierSymbol, - LanguageServiceHost, - mapDefined, - ModuleSpecifierCache, - ModuleSpecifierResolutionHost, - moduleSpecifiers, - nodeModulesPathPart, - PackageJsonImportFilter, - Path, - Program, skipAlias, skipOuterExpressions, - SourceFile, - startsWith, - Statement, - stringContains, stripQuotes, - Symbol, - SymbolFlags, - timestamp, - tryCast, - TypeChecker, +} from "../compiler/utilities"; +import { + isExternalModuleNameRelative, unescapeLeadingUnderscores, - unmangleScopedPackageName, - UserPreferences, -} from "./_namespaces/ts"; +} from "../compiler/utilitiesPublic"; +import { + ExportInfoMap, + ExportKind, + LanguageServiceHost, + SymbolExportInfo, +} from "./types"; +import { + consumesNodeCoreModules, + getNameForExportedSymbol, + getNamesForExportedSymbol, + isExternalModuleSymbol, + PackageJsonImportFilter, +} from "./utilities"; /** @internal */ export const enum ImportKind { @@ -69,26 +90,6 @@ export const enum ImportKind { CommonJS, } -/** @internal */ -export const enum ExportKind { - Named, - Default, - ExportEquals, - UMD, -} - -/** @internal */ -export interface SymbolExportInfo { - readonly symbol: Symbol; - readonly moduleSymbol: Symbol; - /** Set if `moduleSymbol` is an external module, not an ambient module */ - moduleFileName: string | undefined; - exportKind: ExportKind; - targetFlags: SymbolFlags; - /** True if export was only found via the package.json AutoImportProvider (for telemetry). */ - isFromPackageJson: boolean; -} - interface CachedSymbolExportInfo { // Used to rehydrate `symbol` and `moduleSymbol` when transient id: number; @@ -108,19 +109,6 @@ interface CachedSymbolExportInfo { isFromPackageJson: boolean; } -/** @internal */ -export interface ExportInfoMap { - isUsableByFile(importingFile: Path): boolean; - clear(): void; - add(importingFile: Path, symbol: Symbol, key: __String, moduleSymbol: Symbol, moduleFile: SourceFile | undefined, exportKind: ExportKind, isFromPackageJson: boolean, checker: TypeChecker): void; - get(importingFile: Path, key: string): readonly SymbolExportInfo[] | undefined; - search(importingFile: Path, preferCapitalized: boolean, matches: (name: string, targetFlags: SymbolFlags) => boolean, action: (info: readonly SymbolExportInfo[], symbolName: string, isFromAmbientModule: boolean, key: string) => T | undefined): T | undefined; - releaseSymbols(): void; - isEmpty(): boolean; - /** @returns Whether the change resulted in the cache being cleared */ - onFileChanged(oldSourceFile: SourceFile, newSourceFile: SourceFile, typeAcquisitionEnabled: boolean): boolean; -} - /** @internal */ export interface CacheableExportInfoMapHost { getCurrentProgram(): Program | undefined; @@ -366,7 +354,7 @@ export function isImportableFile( const getCanonicalFileName = hostGetCanonicalFileName(moduleSpecifierResolutionHost); const globalTypingsCache = moduleSpecifierResolutionHost.getGlobalTypingsCacheLocation?.(); - const hasImportablePath = !!moduleSpecifiers.forEachFileNameOfModule( + const hasImportablePath = !!forEachFileNameOfModule( from.fileName, to.fileName, moduleSpecifierResolutionHost, diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 5f3eb98656546..017b2c6ffda78 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -1,201 +1,107 @@ import { - __String, - addToSeen, append, - AssignmentDeclarationKind, - BinaryExpression, - BindingElement, - Block, - CallExpression, - CancellationToken, - canHaveSymbol, cast, - CheckFlags, - ClassLikeDeclaration, - climbPastPropertyAccess, compareValues, - ConstructorDeclaration, contains, createQueue, - createTextSpan, - createTextSpanFromBounds, - createTextSpanFromRange, - Debug, - Declaration, - displayPart, - DocumentSpan, emptyArray, - emptyOptions, - escapeLeadingUnderscores, - ExportSpecifier, - Expression, - externalHelpersModuleNameText, - FileIncludeReason, - FileReference, filter, find, - findAncestor, - findChildOfKind, findIndex, first, firstDefined, firstOrUndefined, flatMap, - forEachChild, - forEachChildRecursively, - forEachReturnStatement, - ForInOrOfStatement, - FunctionDeclaration, - FunctionExpression, - FunctionLikeDeclaration, - GetAccessorDeclaration, - getAdjustedReferenceLocation, - getAdjustedRenameLocation, - getAllSuperTypeNodes, - getAncestor, - getAssignmentDeclarationKind, - getCheckFlags, - getContainerNode, - getContainingObjectLiteralElement, - getContextualTypeFromParentOrAncestorTypeNode, - getDeclarationFromName, - getDeclarationOfKind, - getEffectiveModifierFlags, - getLocalSymbolForExportDefault, - getMeaningFromDeclaration, - getMeaningFromLocation, - getModeForUsageLocation, - getNameOfDeclaration, - getNameTable, - getNextJSDocCommentLocation, - getNodeId, - getNodeKind, - getPropertySymbolFromBindingElement, - getPropertySymbolsFromContextualType, - getQuoteFromPreference, - getReferencedFileLocation, - getSuperContainer, - getSymbolId, - getSyntacticModifierFlags, - getTargetLabel, - getTextOfNode, - getThisContainer, - getTouchingPropertyName, - GoToDefinition, - hasEffectiveModifier, - hasInitializer, - hasSyntacticModifier, - hasType, - HighlightSpan, - HighlightSpanKind, - Identifier, - ImplementationLocation, - InterfaceDeclaration, - InternalSymbolName, - isAccessExpression, - isArrayLiteralOrObjectLiteralDestructuringPattern, - isAssertionExpression, + map, + mapDefined, + MultiMap, + some, + tryAddToSet, + tryCast, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { isBinaryExpression, - isBindableObjectDefinePropertyCall, isBindingElement, - isBreakOrContinueStatement, isCallExpression, - isCallExpressionTarget, isCatchClause, - isClassLike, isClassStaticBlockDeclaration, isComputedPropertyName, isConstructorDeclaration, - isDeclaration, - isDeclarationName, isExportAssignment, isExportSpecifier, - isExpressionOfExternalModuleImportEqualsDeclaration, - isExpressionStatement, isExpressionWithTypeArguments, - isExternalModule, - isExternalModuleSymbol, - isExternalOrCommonJsModule, - isForInOrOfStatement, isFunctionExpression, - isFunctionLike, - isFunctionLikeDeclaration, isIdentifier, - isIdentifierPart, - isImportMeta, - isImportOrExportSpecifier, isImportSpecifier, isImportTypeNode, - isInJSFile, - isInNonReferenceComment, - isInString, isInterfaceDeclaration, isJSDocMemberName, - isJSDocTag, isJsxClosingElement, isJsxElement, isJsxFragment, isJsxOpeningElement, isJsxSelfClosingElement, - isJumpStatementTarget, isLabeledStatement, - isLabelOfLabeledStatement, - isLiteralComputedPropertyDeclarationName, - isLiteralNameOfPropertyDeclarationOrIndexAccess, isLiteralTypeNode, - isMethodOrAccessor, isModuleDeclaration, - isModuleExportsAccessExpression, - isModuleOrEnumDeclaration, - isModuleSpecifierLike, - isNameOfModuleDeclaration, isNamespaceExportDeclaration, - isNewExpressionTarget, isNoSubstitutionTemplateLiteral, isNumericLiteral, - isObjectBindingElementWithoutPropertyName, isObjectLiteralExpression, - isObjectLiteralMethod, isParameter, - isParameterPropertyDeclaration, - isPrivateIdentifierClassElementDeclaration, isPropertyAccessExpression, isQualifiedName, - isReferencedFile, - isReferenceFileLocation, - isRightSideOfPropertyAccess, isShorthandPropertyAssignment, isSourceFile, - isStatement, - isStatic, isStaticModifier, - isStringLiteralLike, - isSuperProperty, - isThis, isTypeAliasDeclaration, - isTypeElement, - isTypeKeyword, isTypeLiteralNode, - isTypeNode, isTypeOperatorNode, isUnionTypeNode, - isVariableDeclarationInitializedToBareOrAccessedRequire, - isVariableDeclarationList, - isVariableLike, - isVariableStatement, isVoidExpression, - isWriteAccess, +} from "../compiler/factory/nodeTests"; +import { forEachChild, forEachChildRecursively } from "../compiler/parser"; +import { forEachReturnStatement } from "../compiler/parserUtilities"; +import { + getModeForUsageLocation, + getReferencedFileLocation, + isReferencedFile, + isReferenceFileLocation, +} from "../compiler/program"; +import { + isIdentifierPart, + tokenToString, +} from "../compiler/scanner"; +import { + __String, + AssignmentDeclarationKind, + BindingElement, + Block, + CallExpression, + CancellationToken, + CheckFlags, + ClassLikeDeclaration, + ConstructorDeclaration, + Declaration, + ExportSpecifier, + Expression, + FileIncludeReason, + FileReference, + FunctionDeclaration, + FunctionExpression, + FunctionLikeDeclaration, + GetAccessorDeclaration, + Identifier, + InterfaceDeclaration, + InternalSymbolName, JSDocTag, - map, - mapDefined, MethodDeclaration, ModifierFlags, ModuleDeclaration, - MultiMap, NamedDeclaration, Node, NodeFlags, - nodeSeenTracker, NumericLiteral, ObjectLiteralExpression, ParameterDeclaration, @@ -206,47 +112,92 @@ import { PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, - punctuationPart, - QuotePreference, - rangeIsOnSingleLine, - ReferencedSymbol, - ReferencedSymbolDefinitionInfo, - ReferencedSymbolEntry, - ReferenceEntry, - RenameLocation, - ScriptElementKind, ScriptTarget, - SemanticMeaning, SetAccessorDeclaration, SignatureDeclaration, - skipAlias, - some, SourceFile, Statement, StringLiteral, StringLiteralLike, - stripQuotes, - SuperContainer, Symbol, - SymbolDisplay, - SymbolDisplayPart, - SymbolDisplayPartKind, SymbolFlags, SymbolId, - symbolName, SyntaxKind, - textPart, TextSpan, - tokenToString, TransformFlags, - tryAddToSet, - tryCast, - tryGetClassExtendingExpressionWithTypeArguments, - tryGetImportFromModuleSpecifier, TypeChecker, TypeLiteralNode, VariableDeclaration, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + addToSeen, + externalHelpersModuleNameText, + getAllSuperTypeNodes, + getAncestor, + getAssignmentDeclarationKind, + getCheckFlags, + getDeclarationFromName, + getDeclarationOfKind, + getEffectiveModifierFlags, + getLocalSymbolForExportDefault, + getNextJSDocCommentLocation, + getNodeId, + getSuperContainer, + getSymbolId, + getSyntacticModifierFlags, + getTextOfNode, + getThisContainer, + hasEffectiveModifier, + hasSyntacticModifier, + isAccessExpression, + isBindableObjectDefinePropertyCall, + isDeclarationName, + isExternalModule, + isExternalOrCommonJsModule, + isImportMeta, + isInJSFile, + isLiteralComputedPropertyDeclarationName, + isModuleExportsAccessExpression, + isObjectLiteralMethod, + isStatic, + isSuperProperty, + isVariableDeclarationInitializedToBareOrAccessedRequire, + isVariableLike, + isWriteAccess, + rangeIsOnSingleLine, + skipAlias, + stripQuotes, + SuperContainer, + tryGetClassExtendingExpressionWithTypeArguments, + tryGetImportFromModuleSpecifier, +} from "../compiler/utilities"; +import { + canHaveSymbol, + createTextSpan, + escapeLeadingUnderscores, + findAncestor, + getNameOfDeclaration, + hasInitializer, + hasType, + isAssertionExpression, + isBreakOrContinueStatement, + isClassLike, + isDeclaration, + isFunctionLike, + isFunctionLikeDeclaration, + isImportOrExportSpecifier, + isJSDocTag, + isMethodOrAccessor, + isModuleOrEnumDeclaration, + isParameterPropertyDeclaration, + isPrivateIdentifierClassElementDeclaration, + isStatement, + isStringLiteralLike, + isTypeElement, + isTypeNode, + symbolName, +} from "../compiler/utilitiesPublic"; +import { getReferenceAtPosition } from "./goToDefinition"; import { createImportTracker, ExportInfo, @@ -258,7 +209,79 @@ import { ImportsResult, ImportTracker, ModuleReference, -} from "./_namespaces/ts.FindAllReferences"; +} from "./importTracker"; +import { + getContainingObjectLiteralElement, + getNameTable, + getPropertySymbolsFromContextualType, +} from "./services"; +import { + getSymbolDisplayPartsDocumentationAndSymbolKind, +} from "./symbolDisplay"; +import { + ContextNode, + ContextWithStartAndEndNode, + DocumentSpan, + emptyOptions, + Entry, + EntryKind, + HighlightSpan, + HighlightSpanKind, + ImplementationLocation, + NodeEntry, + NodeEntryKind, + ReferencedSymbol, + ReferencedSymbolDefinitionInfo, + ReferencedSymbolEntry, + ReferenceEntry, + RenameLocation, + ScriptElementKind, + SpanEntry, + SymbolDisplayPart, + SymbolDisplayPartKind, +} from "./types"; +import { + climbPastPropertyAccess, + createTextSpanFromRange, + displayPart, + findChildOfKind, + getAdjustedReferenceLocation, + getAdjustedRenameLocation, + getContainerNode, + getContextNode, + getContextualTypeFromParentOrAncestorTypeNode, + getMeaningFromDeclaration, + getMeaningFromLocation, + getNodeKind, + getPropertySymbolFromBindingElement, + getQuoteFromPreference, + getTargetLabel, + getTextSpan, + getTextSpanOfEntry, + getTouchingPropertyName, + isArrayLiteralOrObjectLiteralDestructuringPattern, + isCallExpressionTarget, + isExpressionOfExternalModuleImportEqualsDeclaration, + isExternalModuleSymbol, + isInNonReferenceComment, + isInString, + isJumpStatementTarget, + isLabelOfLabeledStatement, + isLiteralNameOfPropertyDeclarationOrIndexAccess, + isModuleSpecifierLike, + isNameOfModuleDeclaration, + isNewExpressionTarget, + isObjectBindingElementWithoutPropertyName, + isRightSideOfPropertyAccess, + isThis, + isTypeKeyword, + nodeSeenTracker, + punctuationPart, + QuotePreference, + SemanticMeaning, + textPart, + toContextSpan, +} from "./utilities"; /** @internal */ export interface SymbolAndEntries { @@ -277,31 +300,6 @@ export type Definition = | { readonly type: DefinitionKind.String; readonly node: StringLiteralLike } | { readonly type: DefinitionKind.TripleSlashReference; readonly reference: FileReference, readonly file: SourceFile }; -/** @internal */ -export const enum EntryKind { Span, Node, StringLiteral, SearchedLocalFoundProperty, SearchedPropertyFoundLocal } -/** @internal */ -export type NodeEntryKind = EntryKind.Node | EntryKind.StringLiteral | EntryKind.SearchedLocalFoundProperty | EntryKind.SearchedPropertyFoundLocal; -/** @internal */ -export type Entry = NodeEntry | SpanEntry; -/** @internal */ -export interface ContextWithStartAndEndNode { - start: Node; - end: Node; -} -/** @internal */ -export type ContextNode = Node | ContextWithStartAndEndNode; -/** @internal */ -export interface NodeEntry { - readonly kind: NodeEntryKind; - readonly node: Node; - readonly context?: ContextNode; -} -/** @internal */ -export interface SpanEntry { - readonly kind: EntryKind.Span; - readonly fileName: string; - readonly textSpan: TextSpan; -} /** @internal */ export function nodeEntry(node: Node, kind: NodeEntryKind = EntryKind.Node): NodeEntry { return { @@ -311,10 +309,6 @@ export function nodeEntry(node: Node, kind: NodeEntryKind = EntryKind.Node): Nod }; } -/** @internal */ -export function isContextWithStartAndEndNode(node: ContextNode): node is ContextWithStartAndEndNode { - return node && (node as Node).kind === undefined; -} function getContextNodeForNodeEntry(node: Node): ContextNode | undefined { if (isDeclaration(node)) { @@ -382,71 +376,6 @@ function getContextNodeForNodeEntry(node: Node): ContextNode | undefined { return undefined; } -/** @internal */ -export function getContextNode(node: NamedDeclaration | BinaryExpression | ForInOrOfStatement | undefined): ContextNode | undefined { - if (!node) return undefined; - switch (node.kind) { - case SyntaxKind.VariableDeclaration: - return !isVariableDeclarationList(node.parent) || node.parent.declarations.length !== 1 ? - node : - isVariableStatement(node.parent.parent) ? - node.parent.parent : - isForInOrOfStatement(node.parent.parent) ? - getContextNode(node.parent.parent) : - node.parent; - - case SyntaxKind.BindingElement: - return getContextNode(node.parent.parent as NamedDeclaration); - - case SyntaxKind.ImportSpecifier: - return node.parent.parent.parent; - - case SyntaxKind.ExportSpecifier: - case SyntaxKind.NamespaceImport: - return node.parent.parent; - - case SyntaxKind.ImportClause: - case SyntaxKind.NamespaceExport: - return node.parent; - - case SyntaxKind.BinaryExpression: - return isExpressionStatement(node.parent) ? - node.parent : - node; - - case SyntaxKind.ForOfStatement: - case SyntaxKind.ForInStatement: - return { - start: (node as ForInOrOfStatement).initializer, - end: (node as ForInOrOfStatement).expression - }; - - case SyntaxKind.PropertyAssignment: - case SyntaxKind.ShorthandPropertyAssignment: - return isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent) ? - getContextNode( - findAncestor(node.parent, node => - isBinaryExpression(node) || isForInOrOfStatement(node) - ) as BinaryExpression | ForInOrOfStatement - ) : - node; - - default: - return node; - } -} - -/** @internal */ -export function toContextSpan(textSpan: TextSpan, sourceFile: SourceFile, context?: ContextNode): { contextSpan: TextSpan } | undefined { - if (!context) return undefined; - const contextSpan = isContextWithStartAndEndNode(context) ? - getTextSpan(context.start, sourceFile, context.end) : - getTextSpan(context, sourceFile); - return contextSpan.start !== textSpan.start || contextSpan.length !== textSpan.length ? - { contextSpan } : - undefined; -} - /** @internal */ export const enum FindReferencesUse { /** @@ -619,7 +548,7 @@ function definitionToReferencedSymbolDefinitionInfo(def: Definition, checker: Ty case DefinitionKind.This: { const { node } = def; const symbol = checker.getSymbolAtLocation(node); - const displayParts = symbol && SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind( + const displayParts = symbol && getSymbolDisplayPartsDocumentationAndSymbolKind( checker, symbol, node.getSourceFile(), getContainerNode(node), node).displayParts || [textPart("this")]; return { ...getFileAndTextSpanFromNode(node), name: "this", kind: ScriptElementKind.variableElement, displayParts }; } @@ -671,7 +600,7 @@ function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker, const meaning = Core.getIntersectingMeaningFromDeclarations(node, symbol); const enclosingDeclaration = symbol.declarations && firstOrUndefined(symbol.declarations) || node; const { displayParts, symbolKind } = - SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, enclosingDeclaration.getSourceFile(), enclosingDeclaration, enclosingDeclaration, meaning); + getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, enclosingDeclaration.getSourceFile(), enclosingDeclaration, enclosingDeclaration, meaning); return { displayParts, kind: symbolKind }; } @@ -831,23 +760,6 @@ export function toHighlightSpan(entry: Entry): { fileName: string, span: Highlig return { fileName: documentSpan.fileName, span }; } -function getTextSpan(node: Node, sourceFile: SourceFile, endNode?: Node): TextSpan { - let start = node.getStart(sourceFile); - let end = (endNode || node).getEnd(); - if (isStringLiteralLike(node) && (end - start) > 2) { - Debug.assert(endNode === undefined); - start += 1; - end -= 1; - } - return createTextSpanFromBounds(start, end); -} - -/** @internal */ -export function getTextSpanOfEntry(entry: Entry) { - return entry.kind === EntryKind.Span ? entry.textSpan : - getTextSpan(entry.node, entry.node.getSourceFile()); -} - /** A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment */ function isWriteAccessForReference(node: Node): boolean { const decl = getDeclarationFromName(node); @@ -941,7 +853,7 @@ export namespace Core { export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlySet = new Set(sourceFiles.map(f => f.fileName))): readonly SymbolAndEntries[] | undefined { node = getAdjustedNode(node, options); if (isSourceFile(node)) { - const resolvedRef = GoToDefinition.getReferenceAtPosition(node, position, program); + const resolvedRef = getReferenceAtPosition(node, position, program); if (!resolvedRef?.file) { return undefined; } diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index cce023cc0e5fd..02843d7f8ccf7 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -1,118 +1,98 @@ +import { + find, + findIndex, + forEachRight, + isLineBreak, + isWhiteSpaceSingleLine, + last, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { isDecorator } from "../../compiler/factory/nodeTests"; +import { canHaveModifiers } from "../../compiler/factory/utilitiesPublic"; +import { forEachChild } from "../../compiler/parser"; import { Block, CallExpression, - canHaveModifiers, CatchClause, CharacterCodes, ClassDeclaration, - CommentRange, - concatenate, - createTextChangeFromStartLength, - Debug, Declaration, Diagnostic, - EditorSettings, - find, - findAncestor, - findIndex, - findPrecedingToken, - forEachChild, - forEachRight, - FormatCodeSettings, - FormattingHost, FunctionDeclaration, - getEndLinePosition, - getLeadingCommentRangesOfNode, - getLineStartPositionForPosition, - getNameOfDeclaration, - getNewLineOrDefaultFromHost, - getNonDecoratorTokenPosOfNode, - getStartPositionOfLine, - getTokenAtPosition, - getTrailingCommentRanges, - hasDecorators, InterfaceDeclaration, - isComment, - isDecorator, - isGrammarError, - isJSDoc, - isLineBreak, - isModifier, - isNodeArray, - isStringOrRegularExpressionOrTemplateLiteral, - isToken, - isWhiteSpaceSingleLine, LanguageVariant, - last, LineAndCharacter, MethodDeclaration, ModuleDeclaration, Node, NodeArray, + SourceFile, + SourceFileLike, + SyntaxKind, + TextRange, + TypeReferenceNode, +} from "../../compiler/types"; +import { + getEndLinePosition, + getNonDecoratorTokenPosOfNode, + getStartPositionOfLine, + hasDecorators, + isGrammarError, nodeIsMissing, nodeIsSynthesized, - rangeContainsPositionExclusive, +} from "../../compiler/utilities"; +import { + getNameOfDeclaration, + isModifier, + isNodeArray, + isToken, +} from "../../compiler/utilitiesPublic"; +import { + EditorSettings, + FormatCodeSettings, + FormatContext, + FormattingHost, + TextChange, + TextRangeWithKind, + TokenInfo, +} from "../types"; +import { + createTextChangeFromStartLength, + findPrecedingToken, + getLineStartPositionForPosition, + getNewLineOrDefaultFromHost, + isComment, + isStringOrRegularExpressionOrTemplateLiteral, rangeContainsRange, rangeContainsStartEnd, rangeOverlapsWithStartEnd, repeatString, - SourceFile, - SourceFileLike, startEndContainsRange, startEndOverlapsWithStartEnd, - SyntaxKind, - TextChange, - TextRange, - TriviaSyntaxKind, - TypeReferenceNode, -} from "../_namespaces/ts"; +} from "../utilities"; import { FormattingContext, FormattingRequestKind, +} from "./formattingContext"; +import { FormattingScanner, getFormattingScanner, +} from "./formattingScanner"; +import { Rule, RuleAction, RuleFlags, - RulesMap, - SmartIndenter, -} from "../_namespaces/ts.formatting"; - -/** @internal */ -export interface FormatContext { - readonly options: FormatCodeSettings; - readonly getRules: RulesMap; - readonly host: FormattingHost; -} - -/** @internal */ -export interface TextRangeWithKind extends TextRange { - kind: T; -} +} from "./rule"; +import { getRulesMap } from "./rulesMap"; +import { SmartIndenter } from "./smartIndenter"; -/** @internal */ -export type TextRangeWithTriviaKind = TextRangeWithKind; - -/** @internal */ -export interface TokenInfo { - leadingTrivia: TextRangeWithTriviaKind[] | undefined; - token: TextRangeWithKind; - trailingTrivia: TextRangeWithTriviaKind[] | undefined; +const enum Constants { + Unknown = -1 } /** @internal */ -export function createTextRangeWithKind(pos: number, end: number, kind: T): TextRangeWithKind { - const textRangeWithKind: TextRangeWithKind = { pos, end, kind }; - if (Debug.isDebugging) { - Object.defineProperty(textRangeWithKind, "__debugKind", { - get: () => Debug.formatSyntaxKind(kind), - }); - } - return textRangeWithKind; -} - -const enum Constants { - Unknown = -1 +export function getFormatContext(options: FormatCodeSettings, host: FormattingHost): FormatContext { + return { options, getRules: getRulesMap(), host }; } /* @@ -490,7 +470,6 @@ function formatSpanWorker( requestKind: FormattingRequestKind, rangeContainsError: (r: TextRange) => boolean, sourceFile: SourceFileLike): TextChange[] { - // formatting context is used by rules provider const formattingContext = new FormattingContext(sourceFile, requestKind, options); let previousRangeTriviaEnd: number; @@ -1378,48 +1357,6 @@ function formatSpanWorker( const enum LineAction { None, LineAdded, LineRemoved } -/** - * - * @internal - */ -export function getRangeOfEnclosingComment( - sourceFile: SourceFile, - position: number, - precedingToken?: Node | null, - tokenAtPosition = getTokenAtPosition(sourceFile, position), -): CommentRange | undefined { - const jsdoc = findAncestor(tokenAtPosition, isJSDoc); - if (jsdoc) tokenAtPosition = jsdoc.parent; - const tokenStart = tokenAtPosition.getStart(sourceFile); - if (tokenStart <= position && position < tokenAtPosition.getEnd()) { - return undefined; - } - - // eslint-disable-next-line no-null/no-null - precedingToken = precedingToken === null ? undefined : precedingToken === undefined ? findPrecedingToken(position, sourceFile) : precedingToken; - - // Between two consecutive tokens, all comments are either trailing on the former - // or leading on the latter (and none are in both lists). - const trailingRangesOfPreviousToken = precedingToken && getTrailingCommentRanges(sourceFile.text, precedingToken.end); - const leadingCommentRangesOfNextToken = getLeadingCommentRangesOfNode(tokenAtPosition, sourceFile); - const commentRanges = concatenate(trailingRangesOfPreviousToken, leadingCommentRangesOfNextToken); - return commentRanges && find(commentRanges, range => rangeContainsPositionExclusive(range, position) || - // The end marker of a single-line comment does not include the newline character. - // With caret at `^`, in the following case, we are inside a comment (^ denotes the cursor position): - // - // // asdf ^\n - // - // But for closed multi-line comments, we don't want to be inside the comment in the following case: - // - // /* asdf */^ - // - // However, unterminated multi-line comments *do* contain their end. - // - // Internally, we represent the end of the comment at the newline and closing '/', respectively. - // - position === range.end && (range.kind === SyntaxKind.SingleLineCommentTrivia || position === sourceFile.getFullWidth())); -} - function getOpenTokenForList(node: Node, list: readonly Node[]) { switch (node.kind) { case SyntaxKind.Constructor: diff --git a/src/services/formatting/formattingContext.ts b/src/services/formatting/formattingContext.ts index 435b2dec686e1..1296811698f8d 100644 --- a/src/services/formatting/formattingContext.ts +++ b/src/services/formatting/formattingContext.ts @@ -1,12 +1,14 @@ +import * as Debug from "../../compiler/debug"; import { - Debug, - findChildOfKind, - FormatCodeSettings, Node, SourceFileLike, SyntaxKind, -} from "../_namespaces/ts"; -import { TextRangeWithKind } from "../_namespaces/ts.formatting"; +} from "../../compiler/types"; +import { + FormatCodeSettings, + TextRangeWithKind, +} from "../types"; +import { findChildOfKind } from "../utilities"; /** @internal */ export const enum FormattingRequestKind { diff --git a/src/services/formatting/formattingScanner.ts b/src/services/formatting/formattingScanner.ts index 6a8087390461d..2747e0c3aef6a 100644 --- a/src/services/formatting/formattingScanner.ts +++ b/src/services/formatting/formattingScanner.ts @@ -1,26 +1,32 @@ import { append, - createScanner, - Debug, + last, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { isJsxAttribute, isJsxElement, isJsxText, - isKeyword, - isToken, - isTrivia, +} from "../../compiler/factory/nodeTests"; +import { createScanner } from "../../compiler/scanner"; +import { LanguageVariant, - last, Node, NodeArray, ScriptTarget, SyntaxKind, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + isKeyword, + isTrivia, +} from "../../compiler/utilities"; +import { isToken } from "../../compiler/utilitiesPublic"; import { - createTextRangeWithKind, TextRangeWithKind, TextRangeWithTriviaKind, TokenInfo, -} from "../_namespaces/ts.formatting"; +} from "../types"; +import { createTextRangeWithKind } from "../utilities"; const standardScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, LanguageVariant.Standard); const jsxScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, LanguageVariant.JSX); diff --git a/src/services/formatting/rule.ts b/src/services/formatting/rule.ts index 99c7c08e1d904..6fdf99278d214 100644 --- a/src/services/formatting/rule.ts +++ b/src/services/formatting/rule.ts @@ -1,8 +1,6 @@ -import { - emptyArray, - SyntaxKind, -} from "../_namespaces/ts"; -import { FormattingContext } from "../_namespaces/ts.formatting"; +import { emptyArray } from "../../compiler/core"; +import { SyntaxKind } from "../../compiler/types"; +import { FormattingContext } from "./formattingContext"; /** @internal */ export interface Rule { diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 9eb7635db8379..6591ff57dddba 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -1,37 +1,51 @@ import { - BinaryExpression, contains, - findAncestor, - findNextToken, - FormatCodeSettings, - hasDecorators, hasProperty, isArray, - isExpression, - isFunctionLikeKind, +} from "../../compiler/core"; +import { isNumericLiteral, isPropertyAccessExpression, isPropertyDeclaration, isPropertySignature, - isTrivia, +} from "../../compiler/factory/nodeTests"; +import { + BinaryExpression, Node, - positionIsASICandidate, - SemicolonPreference, SyntaxKind, - typeKeywords, YieldExpression, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + hasDecorators, + isTrivia, +} from "../../compiler/utilities"; +import { + findAncestor, + isExpression, + isFunctionLikeKind, +} from "../../compiler/utilitiesPublic"; +import { + FormatCodeSettings, + SemicolonPreference, + TextRangeWithKind, +} from "../types"; +import { + findNextToken, + positionIsASICandidate, + typeKeywords, +} from "../utilities"; import { - anyContext, - ContextPredicate, FormattingContext, FormattingRequestKind, +} from "./formattingContext"; +import { + anyContext, + ContextPredicate, Rule, RuleAction, RuleFlags, - TextRangeWithKind, TokenRange, -} from "../_namespaces/ts.formatting"; +} from "./rule"; /** @internal */ export interface RuleSpec { diff --git a/src/services/formatting/rulesMap.ts b/src/services/formatting/rulesMap.ts index 8970be77c18a5..bfa85087450d7 100644 --- a/src/services/formatting/rulesMap.ts +++ b/src/services/formatting/rulesMap.ts @@ -1,28 +1,24 @@ -import { - Debug, - every, - FormatCodeSettings, - FormattingHost, - SyntaxKind, -} from "../_namespaces/ts"; +import { every } from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { SyntaxKind } from "../../compiler/types"; +import { FormattingContext } from "./formattingContext"; import { anyContext, - FormatContext, - FormattingContext, - getAllRules, Rule, RuleAction, +} from "./rule"; +import { + getAllRules, RuleSpec, -} from "../_namespaces/ts.formatting"; +} from "./rules"; /** @internal */ -export function getFormatContext(options: FormatCodeSettings, host: FormattingHost): FormatContext { - return { options, getRules: getRulesMap(), host }; -} +export type RulesMap = (context: FormattingContext) => readonly Rule[] | undefined; let rulesMapCache: RulesMap | undefined; -function getRulesMap(): RulesMap { +/** @internal */ +export function getRulesMap(): RulesMap { if (rulesMapCache === undefined) { rulesMapCache = createRulesMap(getAllRules()); } @@ -50,8 +46,6 @@ function getRuleActionExclusion(ruleAction: RuleAction): RuleAction { return mask; } -/** @internal */ -export type RulesMap = (context: FormattingContext) => readonly Rule[] | undefined; function createRulesMap(rules: readonly RuleSpec[]): RulesMap { const map = buildMap(rules); return context => { diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index 1ab72e8c99bb2..a0fc21f371a09 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -1,3 +1,18 @@ +import { + contains, + find, + isWhiteSpaceLike, + isWhiteSpaceSingleLine, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { + isCallExpression, + isConditionalExpression, +} from "../../compiler/factory/nodeTests"; +import { + getLineAndCharacterOfPosition, + skipTrivia, +} from "../../compiler/scanner"; import { ArrayBindingPattern, ArrayLiteralExpression, @@ -6,32 +21,10 @@ import { ClassDeclaration, ClassExpression, CommentRange, - contains, - Debug, - EditorSettings, - find, - findChildOfKind, - findListItemInfo, - findNextToken, - findPrecedingToken, - FormatCodeSettings, GetAccessorDeclaration, - getLineAndCharacterOfPosition, - getLineStartPositionForPosition, - getStartPositionOfLine, - getTokenAtPosition, IfStatement, ImportClause, - IndentStyle, InterfaceDeclaration, - isCallExpression, - isCallOrNewExpression, - isConditionalExpression, - isDeclaration, - isStatementButNotDeclaration, - isStringOrRegularExpressionOrTemplateLiteral, - isWhiteSpaceLike, - isWhiteSpaceSingleLine, JSDocTemplateTag, LineAndCharacter, NamedImportsOrExports, @@ -39,11 +32,7 @@ import { NodeArray, ObjectBindingPattern, ObjectLiteralExpression, - positionBelongsToNode, - rangeContainsRange, - rangeContainsStartEnd, SignatureDeclaration, - skipTrivia, SourceFile, SourceFileLike, SyntaxKind, @@ -52,15 +41,35 @@ import { TypeLiteralNode, TypeReferenceNode, VariableDeclarationList, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { getStartPositionOfLine } from "../../compiler/utilities"; import { - getRangeOfEnclosingComment, + isCallOrNewExpression, + isDeclaration, + isStatementButNotDeclaration, +} from "../../compiler/utilitiesPublic"; +import { + EditorSettings, + FormatCodeSettings, + IndentStyle, TextRangeWithKind, -} from "../_namespaces/ts.formatting"; +} from "../types"; +import { + findChildOfKind, + findListItemInfo, + findNextToken, + findPrecedingToken, + getLineStartPositionForPosition, + getRangeOfEnclosingComment, + getTokenAtPosition, + isStringOrRegularExpressionOrTemplateLiteral, + positionBelongsToNode, + rangeContainsRange, + rangeContainsStartEnd, +} from "../utilities"; /** @internal */ export namespace SmartIndenter { - const enum Value { Unknown = -1 } diff --git a/src/services/getEditsForFileRename.ts b/src/services/getEditsForFileRename.ts index ef3a1107fca0b..8e195745a669a 100644 --- a/src/services/getEditsForFileRename.ts +++ b/src/services/getEditsForFileRename.ts @@ -1,56 +1,68 @@ +import { getOptionFromName } from "../compiler/commandLineParser"; import { - combinePaths, createGetCanonicalFileName, - createModuleSpecifierResolutionHost, - createRange, - Debug, emptyArray, endsWith, - ensurePathIsNonModuleName, - Expression, - factory, - FileTextChanges, find, forEach, - formatting, GetCanonicalFileName, - getDirectoryPath, - getFileMatcherPatterns, - getModeForUsageLocation, - getOptionFromName, - getRegexFromPattern, - getRelativePathFromDirectory, - getRelativePathFromFile, - getTsConfigObjectLiteralExpression, - hostUsesCaseSensitiveFileNames, - isAmbientModule, + last, + mapDefined, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { factory } from "../compiler/factory/nodeFactory"; +import { isArrayLiteralExpression, isObjectLiteralExpression, isPropertyAssignment, isSourceFile, isStringLiteral, - LanguageServiceHost, - last, - mapDefined, - ModuleResolutionHost, - moduleSpecifiers, +} from "../compiler/factory/nodeTests"; +import { + getFileMatcherPatterns, + getRegexFromPattern, +} from "../compiler/fileMatcher"; +import { resolveModuleName } from "../compiler/moduleNameResolver"; +import { updateModuleSpecifier } from "../compiler/moduleSpecifiers"; +import { + combinePaths, + ensurePathIsNonModuleName, + getDirectoryPath, + getRelativePathFromDirectory, + getRelativePathFromFile, normalizePath, - Path, pathIsRelative, +} from "../compiler/path"; +import { getModeForUsageLocation } from "../compiler/program"; +import { + Expression, + ModuleResolutionHost, + Path, Program, PropertyAssignment, ResolvedModuleWithFailedLookupLocations, - resolveModuleName, SourceFile, SourceFileLike, - SourceMapper, StringLiteralLike, Symbol, - textChanges, TextRange, - tryRemoveDirectoryPrefix, UserPreferences, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + createRange, + getTsConfigObjectLiteralExpression, + hostUsesCaseSensitiveFileNames, + isAmbientModule, + tryRemoveDirectoryPrefix, +} from "../compiler/utilities"; +import { SourceMapper } from "./sourcemaps"; +import { ChangeTracker } from "./textChanges"; +import { + FileTextChanges, + FormatContext, + LanguageServiceHost, +} from "./types"; +import { createModuleSpecifierResolutionHost } from "./utilities"; /** @internal */ export function getEditsForFileRename( @@ -58,7 +70,7 @@ export function getEditsForFileRename( oldFileOrDirPath: string, newFileOrDirPath: string, host: LanguageServiceHost, - formatContext: formatting.FormatContext, + formatContext: FormatContext, preferences: UserPreferences, sourceMapper: SourceMapper, ): readonly FileTextChanges[] { @@ -66,7 +78,7 @@ export function getEditsForFileRename( const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); const oldToNew = getPathUpdater(oldFileOrDirPath, newFileOrDirPath, getCanonicalFileName, sourceMapper); const newToOld = getPathUpdater(newFileOrDirPath, oldFileOrDirPath, getCanonicalFileName, sourceMapper); - return textChanges.ChangeTracker.with({ host, formatContext, preferences }, changeTracker => { + return ChangeTracker.with({ host, formatContext, preferences }, changeTracker => { updateTsconfigFiles(program, changeTracker, oldToNew, oldFileOrDirPath, newFileOrDirPath, host.getCurrentDirectory(), useCaseSensitiveFileNames); updateImports(program, changeTracker, oldToNew, newToOld, host, getCanonicalFileName); }); @@ -103,7 +115,7 @@ function makeCorrespondingRelativeChange(a0: string, b0: string, a1: string, get return combinePathsSafe(getDirectoryPath(a1), rel); } -function updateTsconfigFiles(program: Program, changeTracker: textChanges.ChangeTracker, oldToNew: PathUpdater, oldFileOrDirPath: string, newFileOrDirPath: string, currentDirectory: string, useCaseSensitiveFileNames: boolean): void { +function updateTsconfigFiles(program: Program, changeTracker: ChangeTracker, oldToNew: PathUpdater, oldFileOrDirPath: string, newFileOrDirPath: string, currentDirectory: string, useCaseSensitiveFileNames: boolean): void { const { configFile } = program.getCompilerOptions(); if (!configFile) return; const configDir = getDirectoryPath(configFile.fileName); @@ -176,7 +188,7 @@ function updateTsconfigFiles(program: Program, changeTracker: textChanges.Change function updateImports( program: Program, - changeTracker: textChanges.ChangeTracker, + changeTracker: ChangeTracker, oldToNew: PathUpdater, newToOld: PathUpdater, host: LanguageServiceHost, @@ -215,7 +227,7 @@ function updateImports( // Need an update if the imported file moved, or the importing file moved and was using a relative path. return toImport !== undefined && (toImport.updated || (importingSourceFileMoved && pathIsRelative(importLiteral.text))) - ? moduleSpecifiers.updateModuleSpecifier(program.getCompilerOptions(), sourceFile, getCanonicalFileName(newImportFromPath) as Path, toImport.newFileName, createModuleSpecifierResolutionHost(program, host), importLiteral.text) + ? updateModuleSpecifier(program.getCompilerOptions(), sourceFile, getCanonicalFileName(newImportFromPath) as Path, toImport.newFileName, createModuleSpecifierResolutionHost(program, host), importLiteral.text) : undefined; }); } @@ -293,7 +305,7 @@ function getSourceFileToImportFromResolved(importLiteral: StringLiteralLike, res } } -function updateImportsWorker(sourceFile: SourceFile, changeTracker: textChanges.ChangeTracker, updateRef: (refText: string) => string | undefined, updateImport: (importLiteral: StringLiteralLike) => string | undefined) { +function updateImportsWorker(sourceFile: SourceFile, changeTracker: ChangeTracker, updateRef: (refText: string) => string | undefined, updateImport: (importLiteral: StringLiteralLike) => string | undefined) { for (const ref of sourceFile.referencedFiles || emptyArray) { // TODO: GH#26162 const updated = updateRef(ref.fileName); if (updated !== undefined && updated !== sourceFile.text.slice(ref.pos, ref.end)) changeTracker.replaceRangeWithText(sourceFile, ref, updated); diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 7282903299f4d..b49f2669a49b4 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -1,102 +1,120 @@ import { - AssignmentDeclarationKind, - AssignmentExpression, - AssignmentOperatorToken, - CallLikeExpression, - canHaveSymbol, concatenate, - createTextSpan, - createTextSpanFromBounds, - createTextSpanFromNode, - createTextSpanFromRange, - Debug, - Declaration, - DefinitionInfo, - DefinitionInfoAndBoundSpan, + concatenate, emptyArray, every, - FileReference, filter, find, - FindAllReferences, - findAncestor, first, flatMap, forEach, - FunctionLikeDeclaration, - getAssignmentDeclarationKind, - getContainingObjectLiteralElement, - getDirectoryPath, - getEffectiveBaseTypeNode, - getInvokedExpression, - getModeForUsageLocation, - getNameFromPropertyName, - getNameOfDeclaration, - getPropertySymbolsFromContextualType, - getTargetLabel, - getTextOfPropertyName, - getTouchingPropertyName, - getTouchingToken, - hasEffectiveModifier, - hasInitializer, - hasStaticModifier, - isAnyImportOrBareOrAccessedRequire, - isAssignmentDeclaration, - isAssignmentExpression, + last, + map, + mapDefined, + some, + tryCast, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { isBindingElement, - isCallLikeExpression, - isCallOrNewExpressionTarget, - isClassElement, isClassExpression, - isClassLike, isClassStaticBlockDeclaration, isConstructorDeclaration, - isDeclarationFileName, - isExternalModuleNameRelative, - isFunctionLike, - isFunctionLikeDeclaration, isFunctionTypeNode, isIdentifier, - isImportMeta, isJSDocOverrideTag, - isJsxOpeningLikeElement, - isJumpStatementTarget, - isModuleSpecifierLike, - isNameOfFunctionDeclaration, - isNewExpressionTarget, isObjectBindingPattern, - isPropertyName, - isRightSideOfPropertyAccess, isStaticModifier, isVariableDeclaration, - last, - map, - mapDefined, +} from "../compiler/factory/nodeTests"; +import { isDeclarationFileName } from "../compiler/parser"; +import { + getDirectoryPath, + resolvePath, +} from "../compiler/path"; +import { getModeForUsageLocation } from "../compiler/program"; +import { skipTrivia } from "../compiler/scanner"; +import { + AssignmentDeclarationKind, + AssignmentExpression, + AssignmentOperatorToken, + CallLikeExpression, + Declaration, + FileReference, + FunctionLikeDeclaration, ModifierFlags, - moveRangePastModifiers, Node, NodeFlags, Program, - resolvePath, - ScriptElementKind, SignatureDeclaration, - skipAlias, - skipParentheses, - skipTrivia, - some, SourceFile, Symbol, - SymbolDisplay, SymbolFlags, SyntaxKind, - textRangeContainsPositionInclusive, TextSpan, - tryCast, - tryGetModuleSpecifierFromDeclaration, Type, TypeChecker, TypeFlags, +} from "../compiler/types"; +import { + getAssignmentDeclarationKind, + getEffectiveBaseTypeNode, + getInvokedExpression, + getTextOfPropertyName, + hasEffectiveModifier, + hasStaticModifier, + isAnyImportOrBareOrAccessedRequire, + isAssignmentDeclaration, + isAssignmentExpression, + isImportMeta, + moveRangePastModifiers, + skipAlias, + skipParentheses, + tryGetModuleSpecifierFromDeclaration, +} from "../compiler/utilities"; +import { + canHaveSymbol, + createTextSpan, + createTextSpanFromBounds, + findAncestor, + getNameOfDeclaration, + hasInitializer, + isCallLikeExpression, + isClassElement, + isClassLike, + isExternalModuleNameRelative, + isFunctionLike, + isFunctionLikeDeclaration, + isJsxOpeningLikeElement, + isPropertyName, + textRangeContainsPositionInclusive, unescapeLeadingUnderscores, -} from "./_namespaces/ts"; +} from "../compiler/utilitiesPublic"; +import { + getContainingObjectLiteralElement, + getPropertySymbolsFromContextualType, +} from "./services"; +import { getSymbolKind } from "./symbolDisplay"; +import { + DefinitionInfo, + DefinitionInfoAndBoundSpan, + ScriptElementKind, +} from "./types"; +import { + createTextSpanFromNode, + createTextSpanFromRange, + getContextNode, + getNameFromPropertyName, + getTargetLabel, + getTouchingPropertyName, + getTouchingToken, + isCallOrNewExpressionTarget, + isJumpStatementTarget, + isModuleSpecifierLike, + isNameOfFunctionDeclaration, + isNewExpressionTarget, + isRightSideOfPropertyAccess, + toContextSpan, +} from "./utilities"; /** @internal */ export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number, searchOtherFilesOnly?: boolean, stopAtAlias?: boolean): readonly DefinitionInfo[] | undefined { @@ -524,7 +542,7 @@ function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: */ export function createDefinitionInfo(declaration: Declaration, checker: TypeChecker, symbol: Symbol, node: Node, unverified?: boolean, failedAliasResolution?: boolean): DefinitionInfo { const symbolName = checker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol - const symbolKind = SymbolDisplay.getSymbolKind(checker, symbol, node); + const symbolKind = getSymbolKind(checker, symbol, node); const containerName = symbol.parent ? checker.symbolToString(symbol.parent, node) : ""; return createDefinitionInfoFromName(checker, declaration, symbolKind, symbolName, containerName, unverified, failedAliasResolution); } @@ -543,10 +561,10 @@ function createDefinitionInfoFromName(checker: TypeChecker, declaration: Declara name: symbolName, containerKind: undefined!, // TODO: GH#18217 containerName, - ...FindAllReferences.toContextSpan( + ...toContextSpan( textSpan, sourceFile, - FindAllReferences.getContextNode(declaration) + getContextNode(declaration) ), isLocal: !isDefinitionVisible(checker, declaration), isAmbient: !!(declaration.flags & NodeFlags.Ambient), diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index f79eb63627f3c..dfb2ad0c0582f 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -1,52 +1,22 @@ import { - __String, - AnyImportOrReExport, - AssignmentDeclarationKind, - BinaryExpression, - BindingElement, - CallExpression, - CancellationToken, - canHaveModifiers, - canHaveSymbol, cast, - Debug, - ExportAssignment, - ExportDeclaration, - FileReference, - findAncestor, forEach, - getAssignmentDeclarationKind, - getFirstIdentifier, - getNameOfAccessExpression, - getSourceFileOfNode, - getSymbolId, - hasSyntacticModifier, - Identifier, - ImportCall, - ImportClause, - ImportDeclaration, - ImportEqualsDeclaration, - importFromModuleSpecifier, - ImportSpecifier, - InternalSymbolName, - isAccessExpression, + some, + tryCast, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { isBinaryExpression, isBindingElement, isCatchClause, - isDefaultImport, isExportAssignment, isExportDeclaration, isExportModifier, isExportSpecifier, - isExternalModuleAugmentation, - isExternalModuleSymbol, - isImportCall, isImportEqualsDeclaration, isImportTypeNode, - isInJSFile, isJSDocCallbackTag, isJSDocTypedefTag, - isModuleExportsAccessExpression, isNamedExports, isNamespaceExport, isPrivateIdentifier, @@ -55,33 +25,73 @@ import { isSourceFile, isStringLiteral, isVariableDeclaration, - isVariableDeclarationInitializedToBareOrAccessedRequire, isVariableStatement, +} from "../compiler/factory/nodeTests"; +import { canHaveModifiers } from "../compiler/factory/utilitiesPublic"; +import { + __String, + AnyImportOrReExport, + AssignmentDeclarationKind, + BinaryExpression, + BindingElement, + CallExpression, + CancellationToken, + ExportAssignment, + ExportDeclaration, + FileReference, + Identifier, + ImportCall, + ImportClause, + ImportDeclaration, + ImportEqualsDeclaration, + ImportSpecifier, + InternalSymbolName, ModifierFlags, ModuleBlock, ModuleDeclaration, NamedImportsOrExports, NamespaceImport, Node, - nodeIsSynthesized, - nodeSeenTracker, Program, - some, SourceFile, Statement, StringLiteral, StringLiteralLike, Symbol, - symbolEscapedNameNoDefault, SymbolFlags, - symbolName, SyntaxKind, - tryCast, TypeChecker, ValidImportTypeNode, VariableDeclaration, +} from "../compiler/types"; +import { + getAssignmentDeclarationKind, + getFirstIdentifier, + getNameOfAccessExpression, + getSourceFileOfNode, + getSymbolId, + hasSyntacticModifier, + importFromModuleSpecifier, + isAccessExpression, + isDefaultImport, + isExternalModuleAugmentation, + isImportCall, + isInJSFile, + isModuleExportsAccessExpression, + isVariableDeclarationInitializedToBareOrAccessedRequire, + nodeIsSynthesized, +} from "../compiler/utilities"; +import { + canHaveSymbol, + findAncestor, + symbolName, walkUpBindingElementsAndPatterns, -} from "./_namespaces/ts"; +} from "../compiler/utilitiesPublic"; +import { + isExternalModuleSymbol, + nodeSeenTracker, + symbolEscapedNameNoDefault, +} from "./utilities"; /* Code for finding imports of an exported symbol. Used only by FindAllReferences. */ diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts index 49b113e927554..7b1cbf08fe6f0 100644 --- a/src/services/inlayHints.ts +++ b/src/services/inlayHints.ts @@ -1,52 +1,43 @@ import { - __String, - ArrowFunction, - CallExpression, - createPrinterWithRemoveComments, - Debug, - EmitHint, - EnumMember, equateStringsCaseInsensitive, - Expression, - findChildOfKind, - forEachChild, - FunctionDeclaration, - FunctionExpression, - FunctionLikeDeclaration, - GetAccessorDeclaration, - getEffectiveReturnTypeNode, - getEffectiveTypeAnnotationNode, - getLanguageVariant, - getLeadingCommentRanges, - hasContextSensitiveParameters, - Identifier, - InlayHint, - InlayHintKind, - InlayHintsContext, + some, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { createPrinterWithRemoveComments } from "../compiler/emitter"; +import { isArrowFunction, - isAssertionExpression, - isBindingPattern, isCallExpression, isEnumMember, isExpressionWithTypeArguments, isFunctionDeclaration, isFunctionExpression, - isFunctionLikeDeclaration, isGetAccessorDeclaration, isIdentifier, - isIdentifierText, - isInfinityOrNaNString, - isLiteralExpression, isMethodDeclaration, isNewExpression, isObjectLiteralExpression, isParameter, - isParameterDeclaration, isPropertyAccessExpression, isPropertyDeclaration, - isTypeNode, - isVarConst, isVariableDeclaration, +} from "../compiler/factory/nodeTests"; +import { forEachChild } from "../compiler/parser"; +import { + getLeadingCommentRanges, + isIdentifierText, +} from "../compiler/scanner"; +import { + __String, + ArrowFunction, + CallExpression, + EmitHint, + EnumMember, + Expression, + FunctionDeclaration, + FunctionExpression, + FunctionLikeDeclaration, + GetAccessorDeclaration, + Identifier, MethodDeclaration, NewExpression, Node, @@ -55,19 +46,40 @@ import { PrefixUnaryExpression, PropertyDeclaration, Signature, - skipParentheses, - some, Symbol, SymbolFlags, SyntaxKind, - textSpanIntersectsWith, Type, TypeFormatFlags, - unescapeLeadingUnderscores, UserPreferences, - usingSingleLineStringWriter, VariableDeclaration, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + getEffectiveReturnTypeNode, + getEffectiveTypeAnnotationNode, + getLanguageVariant, + hasContextSensitiveParameters, + isInfinityOrNaNString, + isParameterDeclaration, + isVarConst, + skipParentheses, + usingSingleLineStringWriter, +} from "../compiler/utilities"; +import { + isAssertionExpression, + isBindingPattern, + isFunctionLikeDeclaration, + isLiteralExpression, + isTypeNode, + textSpanIntersectsWith, + unescapeLeadingUnderscores, +} from "../compiler/utilitiesPublic"; +import { + InlayHint, + InlayHintKind, + InlayHintsContext, +} from "./types"; +import { findChildOfKind } from "./utilities"; const maxHintsLength = 30; diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 263ab35b200cf..08d4995e296d5 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -1,50 +1,42 @@ import { arraysEqual, - ArrowFunction, - AssignmentDeclarationKind, - BinaryExpression, - buildLinkParts, - ClassExpression, - CompletionEntry, - CompletionEntryDetails, - Completions, - ConstructorDeclaration, contains, - Declaration, - DocCommentTemplateOptions, emptyArray, - Expression, - ExpressionStatement, find, - findAncestor, flatMap, flatten, forEach, - forEachAncestor, - forEachReturnStatement, - forEachUnique, - FunctionDeclaration, - FunctionExpression, - getAssignmentDeclarationKind, - getJSDocCommentsAndTags, - getJSDocTags, - getLineStartPositionForPosition, - getTokenAtPosition, - hasJSDocNodes, - hasJSFileExtension, intersperse, + isWhiteSpaceSingleLine, + lastOrUndefined, + length, + map, + mapDefined, + startsWith, +} from "../compiler/core"; +import { hasJSFileExtension } from "../compiler/extension"; +import { isArrowFunction, isBlock, isConstructorDeclaration, - isExpression, isFunctionExpression, - isFunctionLike, - isFunctionLikeDeclaration, isFunctionTypeNode, isIdentifier, isJSDoc, isJSDocParameterTag, - isWhiteSpaceSingleLine, +} from "../compiler/factory/nodeTests"; +import { forEachReturnStatement } from "../compiler/parserUtilities"; +import { + ArrowFunction, + AssignmentDeclarationKind, + BinaryExpression, + ClassExpression, + ConstructorDeclaration, + Declaration, + Expression, + ExpressionStatement, + FunctionDeclaration, + FunctionExpression, JSDoc, JSDocAugmentsTag, JSDocCallbackTag, @@ -55,40 +47,60 @@ import { JSDocSatisfiesTag, JSDocSeeTag, JSDocTag, - JSDocTagInfo, JSDocTemplateTag, JSDocThrowsTag, JSDocTypedefTag, JSDocTypeTag, - lastOrUndefined, - length, - lineBreakPart, - map, - mapDefined, MethodDeclaration, MethodSignature, Node, ParameterDeclaration, - parameterNamePart, ParenthesizedExpression, PropertyAssignment, PropertyDeclaration, - propertyNamePart, PropertySignature, - punctuationPart, - ScriptElementKind, SourceFile, - spacePart, - startsWith, - SymbolDisplayPart, SyntaxKind, + TypeChecker, + VariableStatement, +} from "../compiler/types"; +import { + forEachAncestor, + getAssignmentDeclarationKind, + getJSDocCommentsAndTags, +} from "../compiler/utilities"; +import { + findAncestor, + getJSDocTags, + hasJSDocNodes, + isExpression, + isFunctionLike, + isFunctionLikeDeclaration, +} from "../compiler/utilitiesPublic"; +import { SortText } from "./completions"; +import { + CompletionEntry, + CompletionEntryDetails, + DocCommentTemplateOptions, + JSDocTagInfo, + ScriptElementKind, + SymbolDisplayPart, TextInsertion, +} from "./types"; +import { + buildLinkParts, + forEachUnique, + getLineStartPositionForPosition, + getTokenAtPosition, + lineBreakPart, + parameterNamePart, + propertyNamePart, + punctuationPart, + spacePart, textPart, typeAliasNamePart, - TypeChecker, typeParameterNamePart, - VariableStatement, -} from "./_namespaces/ts"; +} from "./utilities"; const jsDocTagNames = [ "abstract", @@ -353,7 +365,7 @@ export function getJSDocTagNameCompletions(): CompletionEntry[] { name: tagName, kind: ScriptElementKind.keyword, kindModifiers: "", - sortText: Completions.SortText.LocationPriority, + sortText: SortText.LocationPriority, }; })); } @@ -368,7 +380,7 @@ export function getJSDocTagCompletions(): CompletionEntry[] { name: `@${tagName}`, kind: ScriptElementKind.keyword, kindModifiers: "", - sortText: Completions.SortText.LocationPriority + sortText: SortText.LocationPriority }; })); } @@ -405,7 +417,7 @@ export function getJSDocParameterNameCompletions(tag: JSDocParameterTag): Comple return undefined; } - return { name, kind: ScriptElementKind.parameterElement, kindModifiers: "", sortText: Completions.SortText.LocationPriority }; + return { name, kind: ScriptElementKind.parameterElement, kindModifiers: "", sortText: SortText.LocationPriority }; }); } diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts index 873bba76e9278..4396ba191ff1a 100644 --- a/src/services/navigateTo.ts +++ b/src/services/navigateTo.ts @@ -1,32 +1,42 @@ import { - CancellationToken, compareStringsCaseSensitiveUI, compareValues, - createPatternMatcher, - createTextSpanFromNode, - Declaration, emptyArray, +} from "../compiler/core"; +import { isPropertyAccessExpression } from "../compiler/factory/nodeTests"; +import { + CancellationToken, + Declaration, Expression, - getContainerNode, - getNameOfDeclaration, - getNodeKind, - getNodeModifiers, - getTextOfIdentifierOrLiteral, Identifier, ImportClause, ImportEqualsDeclaration, ImportSpecifier, - isPropertyAccessExpression, - isPropertyNameLiteral, - NavigateToItem, Node, - PatternMatcher, - PatternMatchKind, - ScriptElementKind, SourceFile, SyntaxKind, TypeChecker, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + getTextOfIdentifierOrLiteral, + isPropertyNameLiteral, +} from "../compiler/utilities"; +import { getNameOfDeclaration } from "../compiler/utilitiesPublic"; +import { + createPatternMatcher, + PatternMatcher, + PatternMatchKind, +} from "./patternMatcher"; +import { + NavigateToItem, + ScriptElementKind, +} from "./types"; +import { + createTextSpanFromNode, + getContainerNode, + getNodeKind, + getNodeModifiers, +} from "./utilities"; interface RawNavigateToItem { readonly name: string; diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index c0dae08dd2b34..e75f0344f9ffb 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -1,3 +1,41 @@ +import { + compareStringsCaseSensitiveUI, + compareValues, + concatenate, + contains, + filterMutate, + forEach, + lastOrUndefined, + map, + mapDefined, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { removeFileExtension } from "../compiler/extension"; +import { factory } from "../compiler/factory/nodeFactory"; +import { + isArrowFunction, + isBinaryExpression, + isCallExpression, + isClassDeclaration, + isElementAccessExpression, + isExportAssignment, + isFunctionDeclaration, + isFunctionExpression, + isIdentifier, + isModuleBlock, + isModuleDeclaration, + isObjectLiteralExpression, + isPrivateIdentifier, + isPropertyAccessExpression, + isPropertyAssignment, + isVariableDeclaration, +} from "../compiler/factory/nodeTests"; +import { setTextRange } from "../compiler/factory/utilitiesPublic"; +import { forEachChild } from "../compiler/parser"; +import { + getBaseFileName, + normalizePath, +} from "../compiler/path"; import { ArrowFunction, AssignmentDeclarationKind, @@ -11,104 +49,80 @@ import { ClassElement, ClassExpression, ClassLikeDeclaration, - compareStringsCaseSensitiveUI, - compareValues, - concatenate, ConstructorDeclaration, - contains, - createTextSpanFromNode, - createTextSpanFromRange, - Debug, Declaration, DeclarationName, - declarationNameToString, EntityNameExpression, EnumDeclaration, EnumMember, - escapeString, ExportAssignment, Expression, - factory, - filterMutate, - forEach, - forEachChild, FunctionDeclaration, FunctionExpression, FunctionLikeDeclaration, + Identifier, + ImportClause, + InterfaceDeclaration, + InternalSymbolName, + ModifierFlags, + ModuleDeclaration, + Node, + NodeFlags, + PropertyAccessExpression, + PropertyAssignment, + PropertyDeclaration, + PropertyNameLiteral, + ShorthandPropertyAssignment, + SourceFile, + SpreadAssignment, + SyntaxKind, + TextSpan, + TypeElement, + VariableDeclaration, +} from "../compiler/types"; +import { + declarationNameToString, + escapeString, getAssignmentDeclarationKind, - getBaseFileName, getElementOrPropertyAccessName, getFullWidth, - getNameOfDeclaration, getNameOrArgument, - getNodeKind, - getNodeModifiers, getPropertyNameForPropertyNameNode, getSyntacticModifierFlags, getTextOfIdentifierOrLiteral, getTextOfNode, hasDynamicName, - hasJSDocNodes, - Identifier, - idText, - ImportClause, - InterfaceDeclaration, - InternalSymbolName, isAmbientModule, - isArrowFunction, - isBinaryExpression, isBindableStaticAccessExpression, + isExternalModule, + isJSDocTypeAlias, + isPropertyNameLiteral, + isStatic, +} from "../compiler/utilities"; +import { + getNameOfDeclaration, + hasJSDocNodes, + idText, isBindingPattern, - isCallExpression, - isClassDeclaration, isClassLike, isDeclaration, - isElementAccessExpression, - isExportAssignment, isExpression, - isExternalModule, - isFunctionDeclaration, - isFunctionExpression, - isIdentifier, - isJSDocTypeAlias, - isModuleBlock, - isModuleDeclaration, - isObjectLiteralExpression, isParameterPropertyDeclaration, - isPrivateIdentifier, - isPropertyAccessExpression, - isPropertyAssignment, isPropertyName, - isPropertyNameLiteral, - isStatic, isStringLiteralLike, isToken, - isVariableDeclaration, - lastOrUndefined, - map, - mapDefined, - ModifierFlags, - ModuleDeclaration, + unescapeLeadingUnderscores, +} from "../compiler/utilitiesPublic"; +import { NavigationBarItem, NavigationTree, - Node, - NodeFlags, - normalizePath, - PropertyAccessExpression, - PropertyAssignment, - PropertyDeclaration, - PropertyNameLiteral, - removeFileExtension, - setTextRange, - ShorthandPropertyAssignment, - SourceFile, - SpreadAssignment, - SyntaxKind, - TextSpan, - TypeElement, - unescapeLeadingUnderscores, - VariableDeclaration, -} from "./_namespaces/ts"; +} from "./types"; +import { + createTextSpanFromNode, + createTextSpanFromRange, + getNodeKind, + getNodeModifiers, +} from "./utilities"; /** * Matches all whitespace characters in a string. Eg: diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index c972649deaeb7..64173307cb1bd 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -1,72 +1,89 @@ import { - AnyImportOrRequireStatement, arrayIsSorted, binarySearch, compareBooleans, - Comparer, compareStringsCaseInsensitiveEslintCompatible, compareStringsCaseSensitive, compareValues, - Comparison, - createScanner, detectSortCaseSensitivity, - EmitFlags, emptyArray, - ExportDeclaration, - ExportSpecifier, - Expression, - factory, - FileTextChanges, find, - FindAllReferences, firstOrUndefined, flatMap, - formatting, - getNewLineOrDefaultFromHost, getUILocale, group, - Identifier, identity, - ImportClause, - ImportDeclaration, - ImportOrExportSpecifier, - ImportSpecifier, - isAmbientModule, + isString, + length, + map, + MemoizeCache, + memoizeCached, + some, + SortKind, + stableSort, + tryCast, +} from "../compiler/core"; +import { Comparer, Comparison } from "../compiler/corePublic"; +import { setEmitFlags } from "../compiler/factory/emitNode"; +import { factory } from "../compiler/factory/nodeFactory"; +import { isExportDeclaration, - isExternalModuleNameRelative, isExternalModuleReference, isImportDeclaration, isNamedExports, isNamedImports, isNamespaceImport, - isString, isStringLiteral, - isStringLiteralLike, - jsxModeNeedsExplicitImport, - LanguageServiceHost, - length, - map, - MemoizeCache, - memoizeCached, +} from "../compiler/factory/nodeTests"; +import { + createScanner, + Scanner, +} from "../compiler/scanner"; +import { + AnyImportOrRequireStatement, + EmitFlags, + ExportDeclaration, + ExportSpecifier, + Expression, + Identifier, + ImportClause, + ImportDeclaration, + ImportOrExportSpecifier, + ImportSpecifier, NamedImportBindings, NamedImports, NamespaceImport, - OrganizeImportsMode, Program, - rangeIsOnSingleLine, - Scanner, - setEmitFlags, - some, - SortKind, SourceFile, - stableSort, - suppressLeadingTrivia, SyntaxKind, - textChanges, TransformFlags, - tryCast, UserPreferences, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + isAmbientModule, + rangeIsOnSingleLine, +} from "../compiler/utilities"; +import { + isExternalModuleNameRelative, + isStringLiteralLike, +} from "../compiler/utilitiesPublic"; +import { Core as FindAllReferences } from "./findAllReferences"; +import { + ChangeTracker, + LeadingTriviaOption, + TrailingTriviaOption, +} from "./textChanges"; +import { + FileTextChanges, + FormatContext, + LanguageServiceHost, + OrganizeImportsMode, +} from "./types"; +import { + getNewLineOrDefaultFromHost, + jsxModeNeedsExplicitImport, + suppressLeadingTrivia, +} from "./utilities"; /** * Organize imports by: @@ -78,13 +95,13 @@ import { */ export function organizeImports( sourceFile: SourceFile, - formatContext: formatting.FormatContext, + formatContext: FormatContext, host: LanguageServiceHost, program: Program, preferences: UserPreferences, mode: OrganizeImportsMode, ): FileTextChanges[] { - const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext, preferences }); + const changeTracker = ChangeTracker.fromContext({ host, formatContext, preferences }); const shouldSort = mode === OrganizeImportsMode.SortAndCombine || mode === OrganizeImportsMode.All; const shouldCombine = shouldSort; // These are currently inseparable, but I draw a distinction for clarity and in case we add modes in the future. const shouldRemove = mode === OrganizeImportsMode.RemoveUnused || mode === OrganizeImportsMode.All; @@ -155,21 +172,21 @@ export function organizeImports( // Consider the first node to have trailingTrivia as we want to exclude the // "header" comment. changeTracker.deleteNodes(sourceFile, oldImportDecls, { - leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, - trailingTriviaOption: textChanges.TrailingTriviaOption.Include, + leadingTriviaOption: LeadingTriviaOption.Exclude, + trailingTriviaOption: TrailingTriviaOption.Include, }, /*hasTrailingComment*/ true); } else { // Note: Delete the surrounding trivia because it will have been retained in newImportDecls. const replaceOptions = { - leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, // Leave header comment in place - trailingTriviaOption: textChanges.TrailingTriviaOption.Include, + leadingTriviaOption: LeadingTriviaOption.Exclude, // Leave header comment in place + trailingTriviaOption: TrailingTriviaOption.Include, suffix: getNewLineOrDefaultFromHost(host, formatContext.options), }; changeTracker.replaceNodeWithNodes(sourceFile, oldImportDecls[0], newImportDecls, replaceOptions); const hasTrailingComment = changeTracker.nodeHasTrailingComment(sourceFile, oldImportDecls[0], replaceOptions); changeTracker.deleteNodes(sourceFile, oldImportDecls.slice(1), { - trailingTriviaOption: textChanges.TrailingTriviaOption.Include, + trailingTriviaOption: TrailingTriviaOption.Include, }, hasTrailingComment); } } @@ -287,7 +304,7 @@ function removeUnusedImports(oldImports: readonly ImportDeclaration[], sourceFil function isDeclarationUsed(identifier: Identifier) { // The JSX factory symbol is always used if JSX elements are present - even if they are not allowed. return jsxElementsPresent && (identifier.text === jsxNamespace || jsxFragmentFactory && identifier.text === jsxFragmentFactory) && jsxModeNeedsExplicitImport(compilerOptions.jsx) || - FindAllReferences.Core.isSymbolReferencedInFile(identifier, typeChecker, sourceFile); + FindAllReferences.isSymbolReferencedInFile(identifier, typeChecker, sourceFile); } } diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 4d74425ed15c1..214a281b2a665 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -1,38 +1,34 @@ import { - ArrowFunction, - AssertClause, - Block, - CallExpression, - CancellationToken, - CaseClause, - createTextSpanFromBounds, - createTextSpanFromNode, - createTextSpanFromRange, - Debug, - DefaultClause, - findChildOfKind, - getLeadingCommentRanges, - isAnyImportSyntax, + startsWith, + trimString, + trimStringStart, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { isArrayLiteralExpression, isBinaryExpression, isBindingElement, isBlock, isCallExpression, - isCallOrNewExpression, - isClassLike, - isDeclaration, - isFunctionLike, isIfStatement, - isInComment, isInterfaceDeclaration, isJsxText, isModuleBlock, - isNodeArrayMultiLine, isParenthesizedExpression, isPropertyAccessExpression, isReturnStatement, isTupleTypeNode, isVariableStatement, +} from "../compiler/factory/nodeTests"; +import { getLeadingCommentRanges } from "../compiler/scanner"; +import { + ArrowFunction, + AssertClause, + Block, + CallExpression, + CancellationToken, + CaseClause, + DefaultClause, JsxAttributes, JsxElement, JsxFragment, @@ -42,20 +38,36 @@ import { Node, NodeArray, NoSubstitutionTemplateLiteral, - OutliningSpan, - OutliningSpanKind, ParenthesizedExpression, - positionsAreOnSameLine, SignatureDeclaration, SourceFile, - startsWith, SyntaxKind, TemplateExpression, TextSpan, - trimString, - trimStringStart, TryStatement, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + isAnyImportSyntax, + isNodeArrayMultiLine, + positionsAreOnSameLine, +} from "../compiler/utilities"; +import { + createTextSpanFromBounds, + isCallOrNewExpression, + isClassLike, + isDeclaration, + isFunctionLike, +} from "../compiler/utilitiesPublic"; +import { + OutliningSpan, + OutliningSpanKind, +} from "./types"; +import { + createTextSpanFromNode, + createTextSpanFromRange, + findChildOfKind, + isInComment, +} from "./utilities"; /** @internal */ export function collectElements(sourceFile: SourceFile, cancellationToken: CancellationToken): OutliningSpan[] { diff --git a/src/services/patternMatcher.ts b/src/services/patternMatcher.ts index 212369a9df165..69b7e9b2e41d5 100644 --- a/src/services/patternMatcher.ts +++ b/src/services/patternMatcher.ts @@ -1,16 +1,18 @@ import { - CharacterCodes, compareBooleans, compareValues, - Comparison, - createTextSpan, - isUnicodeIdentifierStart, last, min, - ScriptTarget, startsWith, +} from "../compiler/core"; +import { Comparison } from "../compiler/corePublic"; +import { isUnicodeIdentifierStart } from "../compiler/scanner"; +import { + CharacterCodes, + ScriptTarget, TextSpan, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { createTextSpan } from "../compiler/utilitiesPublic"; // Note(cyrusn): this enum is ordered from strongest match type to weakest match type. /** @internal */ diff --git a/src/services/preProcess.ts b/src/services/preProcess.ts index 91bbe396a2fd6..adba79c120eef 100644 --- a/src/services/preProcess.ts +++ b/src/services/preProcess.ts @@ -1,17 +1,21 @@ import { - FileReference, - isKeyword, lastOrUndefined, length, noop, - PragmaContext, - PreProcessedFileInfo, +} from "../compiler/core"; +import { processCommentPragmas, processPragmasIntoFields, - scanner, +} from "../compiler/parser"; +import { + FileReference, + PragmaContext, ScriptTarget, SyntaxKind, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { isKeyword } from "../compiler/utilities"; +import { PreProcessedFileInfo } from "./types"; +import { scanner } from "./utilities"; export function preProcessFile(sourceText: string, readImportFiles = true, detectJavaScriptImports = false): PreProcessedFileInfo { const pragmaContext: PragmaContext = { diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index a540be35b511b..d7d8aadf916b0 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -1,13 +1,15 @@ import { - ApplicableRefactorInfo, arrayFrom, flatMapIterator, +} from "../compiler/core"; +import { refactorKindBeginsWith } from "./refactors/helpers"; +import { + ApplicableRefactorInfo, InteractiveRefactorArguments, Refactor, RefactorContext, RefactorEditInfo, -} from "./_namespaces/ts"; -import { refactorKindBeginsWith } from "./_namespaces/ts.refactor"; +} from "./types"; // A map with the refactor code as key, the refactor itself as value // e.g. nonSuggestableRefactors[refactorCode] -> the refactor you want diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index 930a1b42d443f..caab90bf8465c 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -1,38 +1,48 @@ import { - ApplicableRefactorInfo, - ArrowFunction, - ConciseBody, - copyLeadingComments, - copyTrailingAsLeadingComments, - copyTrailingComments, - Debug, - Diagnostics, emptyArray, - Expression, - factory, first, - getContainingFunction, - getLocaleSpecificMessage, - getTokenAtPosition, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, isBlock, - isExpression, isReturnStatement, - needsParentheses, - rangeContainsRange, - RefactorContext, - RefactorEditInfo, +} from "../../compiler/factory/nodeTests"; +import { + ArrowFunction, + ConciseBody, + Expression, ReturnStatement, SourceFile, SyntaxKind, - textChanges, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { - isRefactorErrorInfo, + getContainingFunction, + getLocaleSpecificMessage, +} from "../../compiler/utilities"; +import { isExpression } from "../../compiler/utilitiesPublic"; +import { registerRefactor } from "../refactorProvider"; +import { ChangeTracker } from "../textChanges"; +import { + ApplicableRefactorInfo, + RefactorContext, + RefactorEditInfo, RefactorErrorInfo, +} from "../types"; +import { + copyLeadingComments, + copyTrailingAsLeadingComments, + copyTrailingComments, + getTokenAtPosition, + needsParentheses, + rangeContainsRange, +} from "../utilities"; +import { + isRefactorErrorInfo, refactorKindBeginsWith, - registerRefactor, -} from "../_namespaces/ts.refactor"; +} from "./helpers"; const refactorName = "Add or remove braces in an arrow function"; const refactorDescription = Diagnostics.Add_or_remove_braces_in_an_arrow_function.message; @@ -114,7 +124,7 @@ function getRefactorEditsToRemoveFunctionBraces(context: RefactorContext, action Debug.fail("invalid action"); } - const edits = textChanges.ChangeTracker.with(context, t => { + const edits = ChangeTracker.with(context, t => { t.replaceNode(file, func.body, body); }); diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 414ef006bc926..f759fa6c847f3 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -1,62 +1,72 @@ import { - ApplicableRefactorInfo, - ArrowFunction, - Block, - ConciseBody, - copyComments, - copyTrailingAsLeadingComments, - Debug, - Diagnostics, emptyArray, - factory, - FileTextChanges, - FindAllReferences, first, - forEachChild, - FunctionExpression, - getCombinedModifierFlags, - getContainingFunction, - getEffectiveModifierFlags, - getLocaleSpecificMessage, - getTokenAtPosition, - Identifier, + length, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, - isClassLike, - isExpression, isFunctionDeclaration, isFunctionExpression, isIdentifier, isReturnStatement, - isThis, isVariableDeclaration, - isVariableDeclarationInVariableStatement, isVariableDeclarationList, isVariableStatement, - length, +} from "../../compiler/factory/nodeTests"; +import { setTextRange } from "../../compiler/factory/utilitiesPublic"; +import { forEachChild } from "../../compiler/parser"; +import { + ArrowFunction, + Block, + ConciseBody, + FunctionExpression, + Identifier, ModifierFlags, Node, Program, - rangeContainsRange, - RefactorActionInfo, - RefactorContext, - RefactorEditInfo, ReturnStatement, - setTextRange, SourceFile, Statement, - suppressLeadingAndTrailingTrivia, - suppressLeadingTrivia, SyntaxKind, - textChanges, TypeChecker, VariableDeclaration, VariableDeclarationList, VariableStatement, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getContainingFunction, + getEffectiveModifierFlags, + getLocaleSpecificMessage, + isVariableDeclarationInVariableStatement, +} from "../../compiler/utilities"; +import { + getCombinedModifierFlags, + isClassLike, + isExpression, +} from "../../compiler/utilitiesPublic"; +import { Core as FindAllReferences } from "../findAllReferences"; +import { registerRefactor } from "../refactorProvider"; +import { ChangeTracker } from "../textChanges"; import { - refactorKindBeginsWith, - registerRefactor, -} from "../_namespaces/ts.refactor"; + ApplicableRefactorInfo, + FileTextChanges, + RefactorActionInfo, + RefactorContext, + RefactorEditInfo, +} from "../types"; +import { + copyComments, + copyTrailingAsLeadingComments, + getTokenAtPosition, + isThis, + rangeContainsRange, + suppressLeadingAndTrailingTrivia, + suppressLeadingTrivia, +} from "../utilities"; +import { refactorKindBeginsWith } from "./helpers"; const refactorName = "Convert arrow function or function expression"; const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_arrow_function_or_function_expression); @@ -263,7 +273,7 @@ function getEditInfoForConvertToAnonymousFunction(context: RefactorContext, func const { file } = context; const body = convertToBlock(func.body); const newNode = factory.createFunctionExpression(func.modifiers, func.asteriskToken, /*name*/ undefined, func.typeParameters, func.parameters, func.type, body); - return textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, newNode)); + return ChangeTracker.with(context, t => t.replaceNode(file, func, newNode)); } function getEditInfoForConvertToNamedFunction(context: RefactorContext, func: FunctionExpression | ArrowFunction, variableInfo: VariableInfo): FileTextChanges[] { @@ -278,10 +288,10 @@ function getEditInfoForConvertToNamedFunction(context: RefactorContext, func: Fu const newNode = factory.createFunctionDeclaration(length(modifiers) ? modifiers : undefined, func.asteriskToken, name, func.typeParameters, func.parameters, func.type, body); if (variableDeclarationList.declarations.length === 1) { - return textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode)); + return ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode)); } else { - return textChanges.ChangeTracker.with(context, t => { + return ChangeTracker.with(context, t => { t.delete(file, variableDeclaration); t.insertNodeAfter(file, statement, newNode); }); @@ -304,7 +314,7 @@ function getEditInfoForConvertToArrowFunction(context: RefactorContext, func: Fu } const newNode = factory.createArrowFunction(func.modifiers, func.typeParameters, func.parameters, func.type, factory.createToken(SyntaxKind.EqualsGreaterThanToken), body); - return textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, newNode)); + return ChangeTracker.with(context, t => t.replaceNode(file, func, newNode)); } function canBeConvertedToExpression(body: Block, head: Statement): head is ReturnStatement { @@ -312,5 +322,5 @@ function canBeConvertedToExpression(body: Block, head: Statement): head is Retur } function isFunctionReferencedInFile(sourceFile: SourceFile, typeChecker: TypeChecker, node: FunctionExpression): boolean { - return !!node.name && FindAllReferences.Core.isSymbolReferencedInFile(node.name, typeChecker, sourceFile); + return !!node.name && FindAllReferences.isSymbolReferencedInFile(node.name, typeChecker, sourceFile); } diff --git a/src/services/refactors/convertExport.ts b/src/services/refactors/convertExport.ts index 3760cd8b31321..16b95737f0716 100644 --- a/src/services/refactors/convertExport.ts +++ b/src/services/refactors/convertExport.ts @@ -1,37 +1,30 @@ import { - ApplicableRefactorInfo, + emptyArray, + first, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { + isExportAssignment, + isIdentifier, + isModuleBlock, + isSourceFile, + isStringLiteral, +} from "../../compiler/factory/nodeTests"; +import { CancellationToken, ClassDeclaration, - Debug, - Diagnostics, - emptyArray, EnumDeclaration, ExportAssignment, ExportSpecifier, - factory, - FindAllReferences, - findModifier, - first, FunctionDeclaration, - getLocaleSpecificMessage, - getParentNodeInSpan, - getRefactorContextSpan, - getSyntacticModifierFlags, - getTokenAtPosition, Identifier, ImportClause, ImportSpecifier, ImportTypeNode, InterfaceDeclaration, InternalSymbolName, - isAmbientModule, - isExportAssignment, - isExternalModuleAugmentation, - isIdentifier, - isModuleBlock, - isSourceFile, - isStringLiteral, - makeImport, ModifierFlags, ModuleBlock, NamespaceDeclaration, @@ -39,23 +32,38 @@ import { NodeFlags, Program, PropertyAccessExpression, - QuotePreference, - quotePreferenceFromString, - RefactorContext, - RefactorEditInfo, SourceFile, Symbol, SyntaxKind, - textChanges, TypeAliasDeclaration, TypeChecker, VariableStatement, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getLocaleSpecificMessage, + getSyntacticModifierFlags, + isAmbientModule, + isExternalModuleAugmentation, +} from "../../compiler/utilities"; +import { Core as FindAllReferences } from "../findAllReferences"; +import { registerRefactor } from "../refactorProvider"; +import { ChangeTracker } from "../textChanges"; import { - isRefactorErrorInfo, + ApplicableRefactorInfo, + RefactorContext, + RefactorEditInfo, RefactorErrorInfo, - registerRefactor, -} from "../_namespaces/ts.refactor"; +} from "../types"; +import { + findModifier, + getParentNodeInSpan, + getRefactorContextSpan, + getTokenAtPosition, + makeImport, + QuotePreference, + quotePreferenceFromString, +} from "../utilities"; +import { isRefactorErrorInfo } from "./helpers"; const refactorName = "Convert export"; @@ -99,7 +107,7 @@ registerRefactor(refactorName, { Debug.assert(actionName === defaultToNamedAction.name || actionName === namedToDefaultAction.name, "Unexpected action name"); const info = getInfo(context); Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info"); - const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, info, t, context.cancellationToken)); + const edits = ChangeTracker.with(context, t => doChange(context.file, context.program, info, t, context.cancellationToken)); return { edits, renameFilename: undefined, renameLocation: undefined }; }, }); @@ -171,12 +179,12 @@ function getInfo(context: RefactorContext, considerPartialSpans = true): ExportI } } -function doChange(exportingSourceFile: SourceFile, program: Program, info: ExportInfo, changes: textChanges.ChangeTracker, cancellationToken: CancellationToken | undefined): void { +function doChange(exportingSourceFile: SourceFile, program: Program, info: ExportInfo, changes: ChangeTracker, cancellationToken: CancellationToken | undefined): void { changeExport(exportingSourceFile, info, changes, program.getTypeChecker()); changeImports(program, info, changes, cancellationToken); } -function changeExport(exportingSourceFile: SourceFile, { wasDefault, exportNode, exportName }: ExportInfo, changes: textChanges.ChangeTracker, checker: TypeChecker): void { +function changeExport(exportingSourceFile: SourceFile, { wasDefault, exportNode, exportName }: ExportInfo, changes: ChangeTracker, checker: TypeChecker): void { if (wasDefault) { if (isExportAssignment(exportNode) && !exportNode.isExportEquals) { const exp = exportNode.expression as Identifier; @@ -198,7 +206,7 @@ function changeExport(exportingSourceFile: SourceFile, { wasDefault, exportNode, case SyntaxKind.VariableStatement: // If 'x' isn't used in this file and doesn't have type definition, `export const x = 0;` --> `export default 0;` const decl = first(exportNode.declarationList.declarations); - if (!FindAllReferences.Core.isSymbolReferencedInFile(exportName, checker, exportingSourceFile) && !decl.type) { + if (!FindAllReferences.isSymbolReferencedInFile(exportName, checker, exportingSourceFile) && !decl.type) { // We checked in `getInfo` that an initializer exists. changes.replaceNode(exportingSourceFile, exportNode, factory.createExportDefault(Debug.checkDefined(decl.initializer, "Initializer was previously known to be present"))); break; @@ -217,10 +225,10 @@ function changeExport(exportingSourceFile: SourceFile, { wasDefault, exportNode, } } -function changeImports(program: Program, { wasDefault, exportName, exportingModuleSymbol }: ExportInfo, changes: textChanges.ChangeTracker, cancellationToken: CancellationToken | undefined): void { +function changeImports(program: Program, { wasDefault, exportName, exportingModuleSymbol }: ExportInfo, changes: ChangeTracker, cancellationToken: CancellationToken | undefined): void { const checker = program.getTypeChecker(); const exportSymbol = Debug.checkDefined(checker.getSymbolAtLocation(exportName), "Export name should resolve to a symbol"); - FindAllReferences.Core.eachExportReference(program.getSourceFiles(), checker, cancellationToken, exportSymbol, exportingModuleSymbol, exportName.text, wasDefault, ref => { + FindAllReferences.eachExportReference(program.getSourceFiles(), checker, cancellationToken, exportSymbol, exportingModuleSymbol, exportName.text, wasDefault, ref => { if (exportName === ref) return; const importingSourceFile = ref.getSourceFile(); if (wasDefault) { @@ -232,7 +240,7 @@ function changeImports(program: Program, { wasDefault, exportName, exportingModu }); } -function changeDefaultToNamedImport(importingSourceFile: SourceFile, ref: Identifier, changes: textChanges.ChangeTracker, exportName: string): void { +function changeDefaultToNamedImport(importingSourceFile: SourceFile, ref: Identifier, changes: ChangeTracker, exportName: string): void { const { parent } = ref; switch (parent.kind) { case SyntaxKind.PropertyAccessExpression: @@ -278,7 +286,7 @@ function changeDefaultToNamedImport(importingSourceFile: SourceFile, ref: Identi } } -function changeNamedToDefaultImport(importingSourceFile: SourceFile, ref: Identifier, changes: textChanges.ChangeTracker): void { +function changeNamedToDefaultImport(importingSourceFile: SourceFile, ref: Identifier, changes: ChangeTracker): void { const parent = ref.parent as PropertyAccessExpression | ImportSpecifier | ExportSpecifier; switch (parent.kind) { case SyntaxKind.PropertyAccessExpression: diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts index da3a66107def6..67138420e4996 100644 --- a/src/services/refactors/convertImport.ts +++ b/src/services/refactors/convertImport.ts @@ -1,54 +1,64 @@ import { - ApplicableRefactorInfo, arrayFrom, - codefix, - Debug, - Diagnostics, emptyArray, - Expression, - factory, - FindAllReferences, - findAncestor, - findNextToken, - getAllowSyntheticDefaultImports, - getLocaleSpecificMessage, getOwnValues, - getParentNodeInSpan, - getRefactorContextSpan, - getTokenAtPosition, - getUniqueName, - Identifier, - ImportClause, - ImportDeclaration, - ImportKind, - ImportSpecifier, + some, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isExportSpecifier, isImportDeclaration, isPropertyAccessExpression, - isPropertyAccessOrQualifiedName, isShorthandPropertyAssignment, isStringLiteral, +} from "../../compiler/factory/nodeTests"; +import { + Expression, + Identifier, + ImportClause, + ImportDeclaration, + ImportSpecifier, NamedImports, NamespaceImport, Program, PropertyAccessExpression, QualifiedName, - RefactorContext, - RefactorEditInfo, ScriptTarget, - some, SourceFile, Symbol, SymbolFlags, SyntaxKind, - textChanges, TypeChecker, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getAllowSyntheticDefaultImports, + getLocaleSpecificMessage, +} from "../../compiler/utilities"; +import { + findAncestor, + isPropertyAccessOrQualifiedName, +} from "../../compiler/utilitiesPublic"; +import { moduleSpecifierToValidIdentifier } from "../codefixes/importAdder"; +import { ImportKind } from "../exportInfoMap"; +import { Core as FindAllReferences } from "../findAllReferences"; +import { registerRefactor } from "../refactorProvider"; +import { ChangeTracker } from "../textChanges"; import { - isRefactorErrorInfo, + ApplicableRefactorInfo, + RefactorContext, + RefactorEditInfo, RefactorErrorInfo, - registerRefactor, -} from "../_namespaces/ts.refactor"; +} from "../types"; +import { + findNextToken, + getParentNodeInSpan, + getRefactorContextSpan, + getTokenAtPosition, + getUniqueName, +} from "../utilities"; +import { isRefactorErrorInfo } from "./helpers"; const refactorName = "Convert import"; @@ -95,7 +105,7 @@ registerRefactor(refactorName, { Debug.assert(some(getOwnValues(actions), action => action.name === actionName), "Unexpected action name"); const info = getImportConversionInfo(context); Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info"); - const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, t, info)); + const edits = ChangeTracker.with(context, t => doChange(context.file, context.program, t, info)); return { edits, renameFilename: undefined, renameLocation: undefined }; } }); @@ -141,7 +151,7 @@ function getShouldUseDefault(program: Program, importClause: ImportClause) { && isExportEqualsModule(importClause.parent.moduleSpecifier, program.getTypeChecker()); } -function doChange(sourceFile: SourceFile, program: Program, changes: textChanges.ChangeTracker, info: ImportConversionInfo): void { +function doChange(sourceFile: SourceFile, program: Program, changes: ChangeTracker, info: ImportConversionInfo): void { const checker = program.getTypeChecker(); if (info.convertTo === ImportKind.Named) { doChangeNamespaceToNamed(sourceFile, checker, changes, info.import, getAllowSyntheticDefaultImports(program.getCompilerOptions())); @@ -151,13 +161,13 @@ function doChange(sourceFile: SourceFile, program: Program, changes: textChanges } } -function doChangeNamespaceToNamed(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, toConvert: NamespaceImport, allowSyntheticDefaultImports: boolean): void { +function doChangeNamespaceToNamed(sourceFile: SourceFile, checker: TypeChecker, changes: ChangeTracker, toConvert: NamespaceImport, allowSyntheticDefaultImports: boolean): void { let usedAsNamespaceOrDefault = false; const nodesToReplace: (PropertyAccessExpression | QualifiedName)[] = []; const conflictingNames = new Map(); - FindAllReferences.Core.eachSymbolReferenceInFile(toConvert.name, checker, sourceFile, id => { + FindAllReferences.eachSymbolReferenceInFile(toConvert.name, checker, sourceFile, id => { if (!isPropertyAccessOrQualifiedName(id.parent)) { usedAsNamespaceOrDefault = true; } @@ -207,7 +217,7 @@ function getLeftOfPropertyAccessOrQualifiedName(propertyAccessOrQualifiedName: P } /** @internal */ -export function doChangeNamedToNamespaceOrDefault(sourceFile: SourceFile, program: Program, changes: textChanges.ChangeTracker, toConvert: NamedImports, shouldUseDefault = getShouldUseDefault(program, toConvert.parent)): void { +export function doChangeNamedToNamespaceOrDefault(sourceFile: SourceFile, program: Program, changes: ChangeTracker, toConvert: NamedImports, shouldUseDefault = getShouldUseDefault(program, toConvert.parent)): void { const checker = program.getTypeChecker(); const importDecl = toConvert.parent.parent; const { moduleSpecifier } = importDecl; @@ -219,13 +229,13 @@ export function doChangeNamedToNamespaceOrDefault(sourceFile: SourceFile, progra toConvertSymbols.add(symbol); } }); - const preferredName = moduleSpecifier && isStringLiteral(moduleSpecifier) ? codefix.moduleSpecifierToValidIdentifier(moduleSpecifier.text, ScriptTarget.ESNext) : "module"; + const preferredName = moduleSpecifier && isStringLiteral(moduleSpecifier) ? moduleSpecifierToValidIdentifier(moduleSpecifier.text, ScriptTarget.ESNext) : "module"; function hasNamespaceNameConflict(namedImport: ImportSpecifier): boolean { // We need to check if the preferred namespace name (`preferredName`) we'd like to use in the refactored code will present a name conflict. // A name conflict means that, in a scope where we would like to use the preferred namespace name, there already exists a symbol with that name in that scope. // We are going to use the namespace name in the scopes the named imports being refactored are referenced, // so we look for conflicts by looking at every reference to those named imports. - return !!FindAllReferences.Core.eachSymbolReferenceInFile(namedImport.name, checker, sourceFile, id => { + return !!FindAllReferences.eachSymbolReferenceInFile(namedImport.name, checker, sourceFile, id => { const symbol = checker.resolveName(preferredName, id, SymbolFlags.All, /*excludeGlobals*/ true); if (symbol) { // There already is a symbol with the same name as the preferred namespace name. if (toConvertSymbols.has(symbol)) { // `preferredName` resolves to a symbol for one of the named import references we are going to transform into namespace import references... @@ -245,7 +255,7 @@ export function doChangeNamedToNamespaceOrDefault(sourceFile: SourceFile, progra for (const element of toConvert.elements) { const propertyName = (element.propertyName || element.name).text; - FindAllReferences.Core.eachSymbolReferenceInFile(element.name, checker, sourceFile, id => { + FindAllReferences.eachSymbolReferenceInFile(element.name, checker, sourceFile, id => { const access = factory.createPropertyAccessExpression(factory.createIdentifier(namespaceImportName), propertyName); if (isShorthandPropertyAssignment(id.parent)) { changes.replaceNode(sourceFile, id.parent, factory.createPropertyAssignment(id.text, access)); diff --git a/src/services/refactors/convertOverloadListToSingleSignature.ts b/src/services/refactors/convertOverloadListToSingleSignature.ts index c0ad2970a6138..9c41c4aa1bf68 100644 --- a/src/services/refactors/convertOverloadListToSingleSignature.ts +++ b/src/services/refactors/convertOverloadListToSingleSignature.ts @@ -1,25 +1,27 @@ import { - ApplicableRefactorInfo, - CallSignatureDeclaration, - ConstructorDeclaration, - ConstructSignatureDeclaration, - Debug, - Diagnostics, - displayPartsToString, - EmitFlags, emptyArray, every, - factory, - findAncestor, - FunctionDeclaration, - getSourceFileOfNode, - getSyntheticLeadingComments, - getTokenAtPosition, - isFunctionLikeDeclaration, - isIdentifier, length, map, mapDefined, + some, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { + getSyntheticLeadingComments, + setEmitFlags, + setSyntheticLeadingComments, +} from "../../compiler/factory/emitNode"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isIdentifier } from "../../compiler/factory/nodeTests"; +import { setTextRange } from "../../compiler/factory/utilitiesPublic"; +import { + CallSignatureDeclaration, + ConstructorDeclaration, + ConstructSignatureDeclaration, + EmitFlags, + FunctionDeclaration, MethodDeclaration, MethodSignature, NamedTupleMember, @@ -27,19 +29,27 @@ import { NodeArray, ParameterDeclaration, Program, - rangeContainsPosition, - RefactorContext, - RefactorEditInfo, - setEmitFlags, - setSyntheticLeadingComments, - setTextRange, - some, SourceFile, SyntaxKind, - textChanges, TupleTypeNode, -} from "../_namespaces/ts"; -import { registerRefactor } from "../_namespaces/ts.refactor"; +} from "../../compiler/types"; +import { getSourceFileOfNode } from "../../compiler/utilities"; +import { + findAncestor, + isFunctionLikeDeclaration, +} from "../../compiler/utilitiesPublic"; +import { registerRefactor } from "../refactorProvider"; +import { displayPartsToString } from "../services"; +import { ChangeTracker } from "../textChanges"; +import { + ApplicableRefactorInfo, + RefactorContext, + RefactorEditInfo, +} from "../types"; +import { + getTokenAtPosition, + rangeContainsPosition, +} from "../utilities"; const refactorName = "Convert overload list to single signature"; const refactorDescription = Diagnostics.Convert_overload_list_to_single_signature.message; @@ -150,7 +160,7 @@ function getRefactorEditsToConvertOverloadsToOneSignature(context: RefactorConte return; // No edits to apply, do nothing } - const edits = textChanges.ChangeTracker.with(context, t => { + const edits = ChangeTracker.with(context, t => { t.replaceNodeRange(file, signatureDecls[0], signatureDecls[signatureDecls.length - 1], updated); }); diff --git a/src/services/refactors/convertParamsToDestructuredObject.ts b/src/services/refactors/convertParamsToDestructuredObject.ts index a86e190d2d8cd..1b566288fac03 100644 --- a/src/services/refactors/convertParamsToDestructuredObject.ts +++ b/src/services/refactors/convertParamsToDestructuredObject.ts @@ -1,79 +1,57 @@ import { - addEmitFlags, - ApplicableRefactorInfo, - ArrowFunction, - BindingElement, - CallExpression, - CancellationToken, - CheckFlags, - ClassDeclaration, - ClassExpression, compareValues, - ConstructorDeclaration, contains, - copyComments, - Debug, deduplicate, - Diagnostics, - ElementAccessExpression, - EmitFlags, emptyArray, equateValues, every, - Expression, - factory, - FindAllReferences, - findAncestor, - findChildOfKind, - findModifier, first, flatMap, - FunctionBody, - FunctionDeclaration, - FunctionExpression, - FunctionLikeDeclaration, - getCheckFlags, - getContainingFunctionDeclaration, - getContainingObjectLiteralElement, - getLocaleSpecificMessage, - getMeaningFromLocation, - getSourceFileOfNode, - getSymbolTarget, - getSynthesizedDeepClone, - getTextOfIdentifierOrLiteral, - getTouchingToken, - getTypeNodeIfAccessible, - Identifier, - isCallOrNewExpression, + last, + map, + sortAndDeduplicate, + tryCast, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { addEmitFlags } from "../../compiler/factory/emitNode"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isClassDeclaration, isConstructorDeclaration, - isDeclaration, isElementAccessExpression, isExportAssignment, isExportSpecifier, - isExpressionWithTypeArgumentsInClassExtendsClause, - isFunctionLikeDeclaration, isIdentifier, isImportClause, isImportEqualsDeclaration, isImportSpecifier, isInterfaceDeclaration, - isJSDocNode, isMethodSignature, isNamespaceImport, - isNewExpressionTarget, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, - isRestParameter, - isSourceFileJS, - isThis, isTypeLiteralNode, - isVarConst, isVariableDeclaration, - LanguageServiceHost, - last, - map, +} from "../../compiler/factory/nodeTests"; +import { + ArrowFunction, + BindingElement, + CallExpression, + CancellationToken, + CheckFlags, + ClassDeclaration, + ClassExpression, + ConstructorDeclaration, + ElementAccessExpression, + EmitFlags, + Expression, + FunctionBody, + FunctionDeclaration, + FunctionExpression, + FunctionLikeDeclaration, + Identifier, MethodDeclaration, MethodSignature, Modifier, @@ -87,24 +65,65 @@ import { PropertyAccessExpression, PropertyAssignment, PropertySignature, - rangeContainsRange, - RefactorContext, - RefactorEditInfo, - SemanticMeaning, ShorthandPropertyAssignment, - sortAndDeduplicate, SourceFile, - suppressLeadingAndTrailingTrivia, Symbol, SyntaxKind, - textChanges, - tryCast, TypeChecker, TypeLiteralNode, TypeNode, VariableDeclaration, -} from "../_namespaces/ts"; -import { registerRefactor } from "../_namespaces/ts.refactor"; +} from "../../compiler/types"; +import { + getCheckFlags, + getContainingFunctionDeclaration, + getLocaleSpecificMessage, + getSourceFileOfNode, + getTextOfIdentifierOrLiteral, + isExpressionWithTypeArgumentsInClassExtendsClause, + isSourceFileJS, + isVarConst, +} from "../../compiler/utilities"; +import { + findAncestor, + isCallOrNewExpression, + isDeclaration, + isFunctionLikeDeclaration, + isJSDocNode, + isRestParameter, +} from "../../compiler/utilitiesPublic"; +import { getReferenceEntriesForNode } from "../findAllReferences"; +import { registerRefactor } from "../refactorProvider"; +import { getContainingObjectLiteralElement } from "../services"; +import { + ChangeTracker, + LeadingTriviaOption, + TrailingTriviaOption, +} from "../textChanges"; +import { + ApplicableRefactorInfo, + Entry, + EntryKind, + LanguageServiceHost, + NodeEntry, + RefactorContext, + RefactorEditInfo, +} from "../types"; +import { + copyComments, + findChildOfKind, + findModifier, + getMeaningFromLocation, + getSymbolTarget, + getSynthesizedDeepClone, + getTouchingToken, + getTypeNodeIfAccessible, + isNewExpressionTarget, + isThis, + rangeContainsRange, + SemanticMeaning, + suppressLeadingAndTrailingTrivia, +} from "../utilities"; const refactorName = "Convert parameters to destructured object"; const minimumParameterLength = 1; @@ -143,7 +162,7 @@ function getRefactorEditsToConvertParametersToDestructuredObject(context: Refact const groupedReferences = getGroupedReferences(functionDeclaration, program, cancellationToken); if (groupedReferences.valid) { - const edits = textChanges.ChangeTracker.with(context, t => doChange(file, program, host, t, functionDeclaration, groupedReferences)); + const edits = ChangeTracker.with(context, t => doChange(file, program, host, t, functionDeclaration, groupedReferences)); return { renameFilename: undefined, renameLocation: undefined, edits }; } @@ -154,7 +173,7 @@ function doChange( sourceFile: SourceFile, program: Program, host: LanguageServiceHost, - changes: textChanges.ChangeTracker, + changes: ChangeTracker, functionDeclaration: ValidFunctionDeclaration, groupedReferences: GroupedReferences): void { const signature = groupedReferences.signature; @@ -175,7 +194,7 @@ function doChange( first(call.arguments), last(call.arguments), newArgument, - { leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll, trailingTriviaOption: textChanges.TrailingTriviaOption.Include }); + { leadingTriviaOption: LeadingTriviaOption.IncludeAll, trailingTriviaOption: TrailingTriviaOption.Include }); } } @@ -189,8 +208,8 @@ function doChange( joiner: ", ", // indentation is set to 0 because otherwise the object parameter will be indented if there is a `this` parameter indentation: 0, - leadingTriviaOption: textChanges.LeadingTriviaOption.IncludeAll, - trailingTriviaOption: textChanges.TrailingTriviaOption.Include + leadingTriviaOption: LeadingTriviaOption.IncludeAll, + trailingTriviaOption: TrailingTriviaOption.Include }); } } @@ -201,7 +220,7 @@ function getGroupedReferences(functionDeclaration: ValidFunctionDeclaration, pro const names = deduplicate([...functionNames, ...classNames], equateValues); const checker = program.getTypeChecker(); - const references = flatMap(names, /*mapfn*/ name => FindAllReferences.getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); + const references = flatMap(names, /*mapfn*/ name => getReferenceEntriesForNode(-1, name, program, program.getSourceFiles(), cancellationToken)); const groupedReferences = groupReferences(references); if (!every(groupedReferences.declarations, /*callback*/ decl => contains(names, decl))) { @@ -210,7 +229,7 @@ function getGroupedReferences(functionDeclaration: ValidFunctionDeclaration, pro return groupedReferences; - function groupReferences(referenceEntries: readonly FindAllReferences.Entry[]): GroupedReferences { + function groupReferences(referenceEntries: readonly Entry[]): GroupedReferences { const classReferences: ClassReferences = { accessExpressions: [], typeUsages: [] }; const groupedReferences: GroupedReferences = { functionCalls: [], declarations: [], classReferences, valid: true }; const functionSymbols = map(functionNames, getSymbolTargetAtLocation); @@ -219,7 +238,7 @@ function getGroupedReferences(functionDeclaration: ValidFunctionDeclaration, pro const contextualSymbols = map(functionNames, name => getSymbolForContextualType(name, checker)); for (const entry of referenceEntries) { - if (entry.kind === FindAllReferences.EntryKind.Span) { + if (entry.kind === EntryKind.Span) { groupedReferences.valid = false; continue; } @@ -334,7 +353,7 @@ function getSymbolForContextualType(node: Node, checker: TypeChecker): Symbol | } } -function entryToImportOrExport(entry: FindAllReferences.NodeEntry): Node | undefined { +function entryToImportOrExport(entry: NodeEntry): Node | undefined { const node = entry.node; if (isImportSpecifier(node.parent) @@ -350,14 +369,14 @@ function entryToImportOrExport(entry: FindAllReferences.NodeEntry): Node | undef return undefined; } -function entryToDeclaration(entry: FindAllReferences.NodeEntry): Node | undefined { +function entryToDeclaration(entry: NodeEntry): Node | undefined { if (isDeclaration(entry.node.parent)) { return entry.node; } return undefined; } -function entryToFunctionCall(entry: FindAllReferences.NodeEntry): CallExpression | NewExpression | undefined { +function entryToFunctionCall(entry: NodeEntry): CallExpression | NewExpression | undefined { if (entry.node.parent) { const functionReference = entry.node; const parent = functionReference.parent; @@ -395,7 +414,7 @@ function entryToFunctionCall(entry: FindAllReferences.NodeEntry): CallExpression return undefined; } -function entryToAccessExpression(entry: FindAllReferences.NodeEntry): ElementAccessExpression | PropertyAccessExpression | undefined { +function entryToAccessExpression(entry: NodeEntry): ElementAccessExpression | PropertyAccessExpression | undefined { if (entry.node.parent) { const reference = entry.node; const parent = reference.parent; @@ -419,7 +438,7 @@ function entryToAccessExpression(entry: FindAllReferences.NodeEntry): ElementAcc return undefined; } -function entryToType(entry: FindAllReferences.NodeEntry): Node | undefined { +function entryToType(entry: NodeEntry): Node | undefined { const reference = entry.node; if (getMeaningFromLocation(reference) === SemanticMeaning.Type || isExpressionWithTypeArgumentsInClassExtendsClause(reference.parent)) { return reference; diff --git a/src/services/refactors/convertStringOrTemplateLiteral.ts b/src/services/refactors/convertStringOrTemplateLiteral.ts index 53ac3c4bb5628..1bca61a700be2 100644 --- a/src/services/refactors/convertStringOrTemplateLiteral.ts +++ b/src/services/refactors/convertStringOrTemplateLiteral.ts @@ -1,42 +1,54 @@ import { - ApplicableRefactorInfo, - BinaryExpression, - BinaryOperator, - copyTrailingAsLeadingComments, - copyTrailingComments, - Debug, - Diagnostics, emptyArray, - Expression, - factory, - findAncestor, - getLocaleSpecificMessage, - getTextOfNode, - getTokenAtPosition, - getTrailingCommentRanges, + map, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isBinaryExpression, isNoSubstitutionTemplateLiteral, isParenthesizedExpression, isStringLiteral, - isStringLiteralLike, isTemplateExpression, isTemplateHead, isTemplateMiddle, - map, +} from "../../compiler/factory/nodeTests"; +import { getTrailingCommentRanges } from "../../compiler/scanner"; +import { + BinaryExpression, + BinaryOperator, + Expression, Node, ParenthesizedExpression, - RefactorContext, - RefactorEditInfo, SourceFile, SyntaxKind, TemplateHead, TemplateMiddle, TemplateSpan, TemplateTail, - textChanges, Token, -} from "../_namespaces/ts"; -import { registerRefactor } from "../_namespaces/ts.refactor"; +} from "../../compiler/types"; +import { + getLocaleSpecificMessage, + getTextOfNode, +} from "../../compiler/utilities"; +import { + findAncestor, + isStringLiteralLike, +} from "../../compiler/utilitiesPublic"; +import { registerRefactor } from "../refactorProvider"; +import { ChangeTracker } from "../textChanges"; +import { + ApplicableRefactorInfo, + RefactorContext, + RefactorEditInfo, +} from "../types"; +import { + copyTrailingAsLeadingComments, + copyTrailingComments, + getTokenAtPosition, +} from "../utilities"; const refactorName = "Convert to template string"; const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_template_string); @@ -111,13 +123,13 @@ function getEditsForToTemplateLiteral(context: RefactorContext, node: Node) { // since suppressTrailingTrivia(maybeBinary) does not work, the trailing comment is removed manually // otherwise it would have the trailing comment twice - return textChanges.ChangeTracker.with(context, t => { + return ChangeTracker.with(context, t => { t.deleteRange(file, trailingRange); t.replaceNode(file, maybeBinary, templateLiteral); }); } else { - return textChanges.ChangeTracker.with(context, t => t.replaceNode(file, maybeBinary, templateLiteral)); + return ChangeTracker.with(context, t => t.replaceNode(file, maybeBinary, templateLiteral)); } } diff --git a/src/services/refactors/convertToOptionalChainExpression.ts b/src/services/refactors/convertToOptionalChainExpression.ts index a86777e97dd63..16c0eb7b6c833 100644 --- a/src/services/refactors/convertToOptionalChainExpression.ts +++ b/src/services/refactors/convertToOptionalChainExpression.ts @@ -1,51 +1,59 @@ +import { emptyArray } from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; import { - ApplicableRefactorInfo, - BinaryExpression, - CallExpression, - ConditionalExpression, - createTextSpanFromBounds, - Debug, - Diagnostics, - ElementAccessExpression, - emptyArray, - Expression, - ExpressionStatement, - factory, - findTokenOnLeftOfPosition, - getLocaleSpecificMessage, - getRefactorContextSpan, - getSingleVariableOfVariableStatement, - getTokenAtPosition, - Identifier, isBinaryExpression, isCallExpression, isConditionalExpression, isElementAccessExpression, isExpressionStatement, isIdentifier, - isOptionalChain, isPropertyAccessExpression, isReturnStatement, - isStringOrNumericLiteralLike, isVariableStatement, +} from "../../compiler/factory/nodeTests"; +import { + BinaryExpression, + CallExpression, + ConditionalExpression, + ElementAccessExpression, + Expression, + ExpressionStatement, + Identifier, Node, PropertyAccessExpression, - RefactorContext, - RefactorEditInfo, ReturnStatement, - skipParentheses, SourceFile, SyntaxKind, - textChanges, TextSpan, TypeChecker, VariableStatement, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getLocaleSpecificMessage, + getSingleVariableOfVariableStatement, + isStringOrNumericLiteralLike, + skipParentheses, +} from "../../compiler/utilities"; import { - isRefactorErrorInfo, + createTextSpanFromBounds, + isOptionalChain, +} from "../../compiler/utilitiesPublic"; +import { registerRefactor } from "../refactorProvider"; +import { ChangeTracker } from "../textChanges"; +import { + ApplicableRefactorInfo, + RefactorContext, + RefactorEditInfo, RefactorErrorInfo, - registerRefactor, -} from "../_namespaces/ts.refactor"; +} from "../types"; +import { + findTokenOnLeftOfPosition, + getRefactorContextSpan, + getTokenAtPosition, +} from "../utilities"; +import { isRefactorErrorInfo } from "./helpers"; const refactorName = "Convert to optional chain expression"; const convertToOptionalChainExpressionMessage = getLocaleSpecificMessage(Diagnostics.Convert_to_optional_chain_expression); @@ -86,7 +94,7 @@ function getRefactorActionsToConvertToOptionalChain(context: RefactorContext): r function getRefactorEditsToConvertToOptionalChain(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { const info = getInfo(context); Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info"); - const edits = textChanges.ChangeTracker.with(context, t => + const edits = ChangeTracker.with(context, t => doChange(context.file, context.program.getTypeChecker(), t, info, actionName) ); return { edits, renameFilename: undefined, renameLocation: undefined }; @@ -329,7 +337,7 @@ function convertOccurrences(checker: TypeChecker, toConvert: Expression, occurre return toConvert; } -function doChange(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, info: OptionalChainInfo, _actionName: string): void { +function doChange(sourceFile: SourceFile, checker: TypeChecker, changes: ChangeTracker, info: OptionalChainInfo, _actionName: string): void { const { finalExpression, occurrences, expression } = info; const firstOccurrence = occurrences[occurrences.length - 1]; const convertedChain = convertOccurrences(checker, finalExpression, occurrences); diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 9efcce3006624..b62675a8663d0 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -1,96 +1,40 @@ import { - __String, - ANONYMOUS, - ApplicableRefactorInfo, arrayFrom, assertType, - BindingElement, - Block, - BlockLike, - BreakStatement, - CancellationToken, - canHaveModifiers, - CharacterCodes, - ClassElement, - ClassLikeDeclaration, - codefix, compareProperties, compareStringsCaseSensitive, compareValues, contains, - ContinueStatement, - createDiagnosticForNode, - createFileDiagnostic, - Debug, - Declaration, - Diagnostic, - DiagnosticCategory, - DiagnosticMessage, - Diagnostics, - EmitFlags, emptyArray, - EntityName, - Expression, - ExpressionStatement, - factory, find, - findAncestor, - findFirstNonJsxWhitespaceToken, - findTokenOnLeftOfPosition, first, firstOrUndefined, - forEachChild, - formatStringFromArgs, - FunctionDeclaration, - FunctionLikeDeclaration, - getContainingClass, - getContainingFunction, - getEffectiveTypeParameterDeclarations, - getEmitScriptTarget, - getEnclosingBlockScopeContainer, - getLocaleSpecificMessage, - getModifiers, - getNodeId, - getParentNodeInSpan, - getRefactorContextSpan, - getRenameLocation, - getSymbolId, - getSynthesizedDeepClone, - getThisContainer, - getUniqueName, - hasEffectiveModifier, - hasSyntacticModifier, - Identifier, - identifierToKeywordKind, isArray, + last, + map, + mapDefined, + singleOrUndefined, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { setEmitFlags } from "../../compiler/factory/emitNode"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, - isAssignmentExpression, isBinaryExpression, isBlock, - isBlockScope, isCaseClause, - isClassLike, isConstructorDeclaration, - isDeclaration, - isDeclarationWithTypeParameters, isElementAccessExpression, - isExpression, - isExpressionNode, isExpressionStatement, - isFunctionBody, isFunctionExpression, - isFunctionLike, - isFunctionLikeDeclaration, isIdentifier, - isInJSFile, - isIterationStatement, isJsxAttribute, isJsxElement, isJsxFragment, isJsxSelfClosingElement, isModuleBlock, isParenthesizedTypeNode, - isPartOfTypeNode, isPrivateIdentifier, isPropertyAccessExpression, isPropertyDeclaration, @@ -98,20 +42,40 @@ import { isReturnStatement, isShorthandPropertyAssignment, isSourceFile, - isStatement, - isStatic, isStringLiteral, isSwitchStatement, - isThis, - isUnaryExpressionWithWrite, isUnionTypeNode, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, +} from "../../compiler/factory/nodeTests"; +import { canHaveModifiers } from "../../compiler/factory/utilitiesPublic"; +import { forEachChild } from "../../compiler/parser"; +import { positionIsSynthesized } from "../../compiler/scannerUtilities"; +import { nullTransformationContext } from "../../compiler/transformer"; +import { + __String, + BindingElement, + Block, + BlockLike, + BreakStatement, + CancellationToken, + CharacterCodes, + ClassElement, + ClassLikeDeclaration, + ContinueStatement, + Declaration, + Diagnostic, + DiagnosticCategory, + DiagnosticMessage, + EmitFlags, + EntityName, + Expression, + ExpressionStatement, + FunctionDeclaration, + FunctionLikeDeclaration, + Identifier, LabeledStatement, - last, - map, - mapDefined, MethodDeclaration, Modifier, ModifierFlags, @@ -120,31 +84,19 @@ import { Node, NodeBuilderFlags, NodeFlags, - nullTransformationContext, ObjectLiteralElementLike, ParameterDeclaration, - positionIsSynthesized, PropertyAccessExpression, - rangeContainsStartEnd, - RefactorActionInfo, - RefactorContext, - RefactorEditInfo, - setEmitFlags, ShorthandPropertyAssignment, SignatureKind, - singleOrUndefined, - skipParentheses, SourceFile, Statement, StringLiteral, - suppressLeadingAndTrailingTrivia, Symbol, SymbolFlags, SyntaxKind, - textChanges, TextRange, TextSpan, - textSpanEnd, TryStatement, Type, TypeChecker, @@ -155,15 +107,76 @@ import { TypeParameter, TypeParameterDeclaration, VariableDeclaration, + VisitResult, +} from "../../compiler/types"; +import { + createDiagnosticForNode, + createFileDiagnostic, + formatStringFromArgs, + getContainingClass, + getContainingFunction, + getEmitScriptTarget, + getEnclosingBlockScopeContainer, + getLocaleSpecificMessage, + getNodeId, + getSymbolId, + getThisContainer, + hasEffectiveModifier, + hasSyntacticModifier, + isAssignmentExpression, + isBlockScope, + isDeclarationWithTypeParameters, + isExpressionNode, + isInJSFile, + isPartOfTypeNode, + isStatic, + skipParentheses, +} from "../../compiler/utilities"; +import { + findAncestor, + getEffectiveTypeParameterDeclarations, + getModifiers, + identifierToKeywordKind, + isClassLike, + isDeclaration, + isExpression, + isFunctionBody, + isFunctionLike, + isFunctionLikeDeclaration, + isIterationStatement, + isStatement, + isUnaryExpressionWithWrite, + textSpanEnd, +} from "../../compiler/utilitiesPublic"; +import { visitEachChild, visitNode, visitNodes, - VisitResult, -} from "../_namespaces/ts"; +} from "../../compiler/visitorPublic"; +import { typeToAutoImportableTypeNode } from "../codefixes/helpers"; +import { createImportAdder } from "../codefixes/importAdder"; +import { registerRefactor } from "../refactorProvider"; +import { ChangeTracker } from "../textChanges"; +import { + ApplicableRefactorInfo, + RefactorActionInfo, + RefactorContext, + RefactorEditInfo, +} from "../types"; import { - refactorKindBeginsWith, - registerRefactor, -} from "../_namespaces/ts.refactor"; + ANONYMOUS, + findFirstNonJsxWhitespaceToken, + findTokenOnLeftOfPosition, + getParentNodeInSpan, + getRefactorContextSpan, + getRenameLocation, + getSynthesizedDeepClone, + getUniqueName, + isThis, + rangeContainsStartEnd, + suppressLeadingAndTrailingTrivia, +} from "../utilities"; +import { refactorKindBeginsWith } from "./helpers"; const refactorName = "Extract Symbol"; @@ -1035,7 +1048,7 @@ function extractFunctionInScope( const checker = context.program.getTypeChecker(); const scriptTarget = getEmitScriptTarget(context.program.getCompilerOptions()); - const importAdder = codefix.createImportAdder(context.file, context.program, context.preferences, context.host); + const importAdder = createImportAdder(context.file, context.program, context.preferences, context.host, /*useAutoImportProvider*/ false); // Make a unique name for the extracted function const file = scope.getSourceFile(); @@ -1054,7 +1067,7 @@ function extractFunctionInScope( let type = checker.getTypeOfSymbolAtLocation(usage.symbol, usage.node); // Widen the type so we don't emit nonsense annotations like "function fn(x: 3) {" type = checker.getBaseTypeOfLiteralType(type); - typeNode = codefix.typeToAutoImportableTypeNode(checker, importAdder, type, scope, scriptTarget, NodeBuilderFlags.NoTruncation); + typeNode = typeToAutoImportableTypeNode(checker, importAdder, type, scope, scriptTarget, NodeBuilderFlags.NoTruncation); } const paramDecl = factory.createParameterDeclaration( @@ -1146,7 +1159,7 @@ function extractFunctionInScope( ); } - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = ChangeTracker.fromContext(context); const minInsertionPos = (isReadonlyArray(range.range) ? last(range.range) : range.range).end; const nodeToInsertBefore = getNodeToInsertFunctionBefore(minInsertionPos, scope); if (nodeToInsertBefore) { @@ -1367,7 +1380,7 @@ function extractConstantInScope( suppressLeadingAndTrailingTrivia(initializer); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = ChangeTracker.fromContext(context); if (isClassLike(scope)) { Debug.assert(!isJS, "Cannot extract to a JS class"); // See CannotExtractToJSClass diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts index 02e5f10ba22d9..a16f0048d12d6 100644 --- a/src/services/refactors/extractType.ts +++ b/src/services/refactors/extractType.ts @@ -1,74 +1,92 @@ import { addRange, - addToSeen, append, - ApplicableRefactorInfo, cast, concatenate, - createTextRangeFromSpan, - Debug, - Diagnostics, - EmitFlags, emptyArray, - factory, - findAncestor, forEach, - forEachChild, - getEffectiveConstraintOfTypeParameter, - getLineAndCharacterOfPosition, - getLocaleSpecificMessage, - getNameFromPropertyName, - getNewLineOrDefaultFromHost, - getPrecedingNonSpaceCharacterPosition, - getRefactorContextSpan, - getRenameLocation, - getTokenAtPosition, - getUniqueName, + pushIfUnique, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { ignoreSourceNewlines, + setEmitFlags, +} from "../../compiler/factory/emitNode"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isConditionalTypeNode, - isFunctionLike, isIdentifier, isInferTypeNode, isIntersectionTypeNode, isJSDoc, isJSDocTypeExpression, isParenthesizedTypeNode, - isSourceFileJS, - isStatement, - isThisIdentifier, isThisTypeNode, isTupleTypeNode, isTypeLiteralNode, - isTypeNode, isTypeParameterDeclaration, isTypePredicateNode, isTypeQueryNode, isTypeReferenceNode, +} from "../../compiler/factory/nodeTests"; +import { setTextRange } from "../../compiler/factory/utilitiesPublic"; +import { forEachChild } from "../../compiler/parser"; +import { + getLineAndCharacterOfPosition, + skipTrivia, +} from "../../compiler/scanner"; +import { + EmitFlags, JSDocTag, JSDocTemplateTag, Node, - nodeOverlapsWithStartEnd, - pushIfUnique, - rangeContainsStartEnd, - RefactorContext, - RefactorEditInfo, - setEmitFlags, - setTextRange, - skipTrivia, SourceFile, SymbolFlags, - textChanges, TextRange, TypeChecker, TypeElement, TypeNode, TypeParameterDeclaration, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + addToSeen, + getLocaleSpecificMessage, + isSourceFileJS, + isThisIdentifier, +} from "../../compiler/utilities"; +import { + findAncestor, + getEffectiveConstraintOfTypeParameter, + isFunctionLike, + isStatement, + isTypeNode, +} from "../../compiler/utilitiesPublic"; +import { registerRefactor } from "../refactorProvider"; import { - isRefactorErrorInfo, + ChangeTracker, + LeadingTriviaOption, + TrailingTriviaOption, +} from "../textChanges"; +import { + ApplicableRefactorInfo, + RefactorContext, + RefactorEditInfo, RefactorErrorInfo, - registerRefactor, -} from "../_namespaces/ts.refactor"; +} from "../types"; +import { + createTextRangeFromSpan, + getNameFromPropertyName, + getNewLineOrDefaultFromHost, + getPrecedingNonSpaceCharacterPosition, + getRefactorContextSpan, + getRenameLocation, + getTokenAtPosition, + getUniqueName, + nodeOverlapsWithStartEnd, + rangeContainsStartEnd, +} from "../utilities"; +import { isRefactorErrorInfo } from "./helpers"; const refactorName = "Extract type"; @@ -127,7 +145,7 @@ registerRefactor(refactorName, { Debug.assert(info && !isRefactorErrorInfo(info), "Expected to find a range to extract"); const name = getUniqueName("NewType", file); - const edits = textChanges.ChangeTracker.with(context, changes => { + const edits = ChangeTracker.with(context, changes => { switch (actionName) { case extractToTypeAliasAction.name: Debug.assert(!info.isJS, "Invalid actionName/JS combo"); @@ -268,7 +286,7 @@ function collectTypeParameters(checker: TypeChecker, selection: TypeNode, enclos } } -function doTypeAliasChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, info: TypeAliasInfo) { +function doTypeAliasChange(changes: ChangeTracker, file: SourceFile, name: string, info: TypeAliasInfo) { const { enclosingNode, selection, typeParameters } = info; const newTypeNode = factory.createTypeAliasDeclaration( @@ -278,10 +296,10 @@ function doTypeAliasChange(changes: textChanges.ChangeTracker, file: SourceFile, selection ); changes.insertNodeBefore(file, enclosingNode, ignoreSourceNewlines(newTypeNode), /*blankLineBetween*/ true); - changes.replaceNode(file, selection, factory.createTypeReferenceNode(name, typeParameters.map(id => factory.createTypeReferenceNode(id.name, /*typeArguments*/ undefined))), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.ExcludeWhitespace }); + changes.replaceNode(file, selection, factory.createTypeReferenceNode(name, typeParameters.map(id => factory.createTypeReferenceNode(id.name, /*typeArguments*/ undefined))), { leadingTriviaOption: LeadingTriviaOption.Exclude, trailingTriviaOption: TrailingTriviaOption.ExcludeWhitespace }); } -function doInterfaceChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, info: InterfaceInfo) { +function doInterfaceChange(changes: ChangeTracker, file: SourceFile, name: string, info: InterfaceInfo) { const { enclosingNode, selection, typeParameters, typeElements } = info; const newTypeNode = factory.createInterfaceDeclaration( @@ -293,10 +311,10 @@ function doInterfaceChange(changes: textChanges.ChangeTracker, file: SourceFile, ); setTextRange(newTypeNode, typeElements[0]?.parent); changes.insertNodeBefore(file, enclosingNode, ignoreSourceNewlines(newTypeNode), /*blankLineBetween*/ true); - changes.replaceNode(file, selection, factory.createTypeReferenceNode(name, typeParameters.map(id => factory.createTypeReferenceNode(id.name, /*typeArguments*/ undefined))), { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: textChanges.TrailingTriviaOption.ExcludeWhitespace }); + changes.replaceNode(file, selection, factory.createTypeReferenceNode(name, typeParameters.map(id => factory.createTypeReferenceNode(id.name, /*typeArguments*/ undefined))), { leadingTriviaOption: LeadingTriviaOption.Exclude, trailingTriviaOption: TrailingTriviaOption.ExcludeWhitespace }); } -function doTypedefChange(changes: textChanges.ChangeTracker, context: RefactorContext, file: SourceFile, name: string, info: ExtractInfo) { +function doTypedefChange(changes: ChangeTracker, context: RefactorContext, file: SourceFile, name: string, info: ExtractInfo) { const { enclosingNode, selection, typeParameters } = info; setEmitFlags(selection, EmitFlags.NoComments | EmitFlags.NoNestedComments); diff --git a/src/services/refactors/generateGetAccessorAndSetAccessor.ts b/src/services/refactors/generateGetAccessorAndSetAccessor.ts index 2982cacd8af7c..101ed75436297 100644 --- a/src/services/refactors/generateGetAccessorAndSetAccessor.ts +++ b/src/services/refactors/generateGetAccessorAndSetAccessor.ts @@ -1,18 +1,21 @@ +import { emptyArray } from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; import { - ApplicableRefactorInfo, - codefix, - Debug, - Diagnostics, - emptyArray, - getRenameLocation, isIdentifier, isParameter, - RefactorContext, -} from "../_namespaces/ts"; +} from "../../compiler/factory/nodeTests"; +import { + generateAccessorFromProperty, + getAccessorConvertiblePropertyAtPosition, +} from "../codefixes/generateAccessors"; +import { registerRefactor } from "../refactorProvider"; import { - isRefactorErrorInfo, - registerRefactor, -} from "../_namespaces/ts.refactor"; + ApplicableRefactorInfo, + RefactorContext, +} from "../types"; +import { getRenameLocation } from "../utilities"; +import { isRefactorErrorInfo } from "./helpers"; const actionName = "Generate 'get' and 'set' accessors"; const actionDescription = Diagnostics.Generate_get_and_set_accessors.message; @@ -26,9 +29,9 @@ registerRefactor(actionName, { kinds: [generateGetSetAction.kind], getEditsForAction: function getRefactorActionsToGenerateGetAndSetAccessors(context, actionName) { if (!context.endPosition) return undefined; - const info = codefix.getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition); + const info = getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition); Debug.assert(info && !isRefactorErrorInfo(info), "Expected applicable refactor info"); - const edits = codefix.generateAccessorFromProperty(context.file, context.program, context.startPosition, context.endPosition, context, actionName); + const edits = generateAccessorFromProperty(context.file, context.program, context.startPosition, context.endPosition, context, actionName); if (!edits) return undefined; const renameFilename = context.file.fileName; @@ -40,7 +43,7 @@ registerRefactor(actionName, { }, getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] { if (!context.endPosition) return emptyArray; - const info = codefix.getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition, context.triggerReason === "invoked"); + const info = getAccessorConvertiblePropertyAtPosition(context.file, context.program, context.startPosition, context.endPosition, context.triggerReason === "invoked"); if (!info) return emptyArray; if (!isRefactorErrorInfo(info)) { diff --git a/src/services/refactors/helpers.ts b/src/services/refactors/helpers.ts index acafb54f1e36a..3e2e60a06ace0 100644 --- a/src/services/refactors/helpers.ts +++ b/src/services/refactors/helpers.ts @@ -1,12 +1,4 @@ - -/** - * Returned by refactor functions when some error message needs to be surfaced to users. - * - * @internal - */ -export interface RefactorErrorInfo { - error: string; -} +import { RefactorErrorInfo } from "../types"; /** * Checks if some refactor info has refactor error info. @@ -24,6 +16,6 @@ export function isRefactorErrorInfo(info: unknown): info is RefactorErrorInfo { * @internal */ export function refactorKindBeginsWith(known: string, requested: string | undefined): boolean { - if(!requested) return true; + if (!requested) return true; return known.substr(0, requested.length) === requested; } diff --git a/src/services/refactors/inferFunctionReturnType.ts b/src/services/refactors/inferFunctionReturnType.ts index ccce34a8a14ac..23f99e320dc93 100644 --- a/src/services/refactors/inferFunctionReturnType.ts +++ b/src/services/refactors/inferFunctionReturnType.ts @@ -1,38 +1,48 @@ import { - ApplicableRefactorInfo, - ArrowFunction, - Diagnostics, emptyArray, - factory, - findAncestor, - findChildOfKind, first, - FunctionDeclaration, - FunctionExpression, - getLocaleSpecificMessage, - getTokenAtPosition, + mapDefined, +} from "../../compiler/core"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrowFunction, isBlock, - isInJSFile, - mapDefined, +} from "../../compiler/factory/nodeTests"; +import { + ArrowFunction, + FunctionDeclaration, + FunctionExpression, MethodDeclaration, Node, NodeBuilderFlags, - RefactorContext, - RefactorEditInfo, SourceFile, SyntaxKind, - textChanges, Type, TypeChecker, TypeNode, -} from "../_namespaces/ts"; +} from "../../compiler/types"; import { - isRefactorErrorInfo, + getLocaleSpecificMessage, + isInJSFile, +} from "../../compiler/utilities"; +import { findAncestor } from "../../compiler/utilitiesPublic"; +import { registerRefactor } from "../refactorProvider"; +import { ChangeTracker } from "../textChanges"; +import { + ApplicableRefactorInfo, + RefactorContext, + RefactorEditInfo, RefactorErrorInfo, +} from "../types"; +import { + findChildOfKind, + getTokenAtPosition, +} from "../utilities"; +import { + isRefactorErrorInfo, refactorKindBeginsWith, - registerRefactor, -} from "../_namespaces/ts.refactor"; +} from "./helpers"; const refactorName = "Infer function return type"; const refactorDescription = Diagnostics.Infer_function_return_type.message; @@ -51,7 +61,7 @@ registerRefactor(refactorName, { function getRefactorEditsToInferReturnType(context: RefactorContext): RefactorEditInfo | undefined { const info = getInfo(context); if (info && !isRefactorErrorInfo(info)) { - const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, t, info.declaration, info.returnTypeNode)); + const edits = ChangeTracker.with(context, t => doChange(context.file, t, info.declaration, info.returnTypeNode)); return { renameFilename: undefined, renameLocation: undefined, edits }; } return undefined; @@ -88,7 +98,7 @@ interface FunctionInfo { returnTypeNode: TypeNode; } -function doChange(sourceFile: SourceFile, changes: textChanges.ChangeTracker, declaration: ConvertibleDeclaration, typeNode: TypeNode) { +function doChange(sourceFile: SourceFile, changes: ChangeTracker, declaration: ConvertibleDeclaration, typeNode: TypeNode) { const closeParen = findChildOfKind(declaration, SyntaxKind.CloseParenToken, sourceFile); const needParens = isArrowFunction(declaration) && closeParen === undefined; const endNode = needParens ? first(declaration.parameters) : closeParen; diff --git a/src/services/refactors/moveToFile.ts b/src/services/refactors/moveToFile.ts index 20ee5c05647d1..6a4eceb18016c 100644 --- a/src/services/refactors/moveToFile.ts +++ b/src/services/refactors/moveToFile.ts @@ -1,128 +1,97 @@ -import { getModuleSpecifier } from "../../compiler/moduleSpecifiers"; import { - AnyImportOrRequireStatement, append, - ApplicableRefactorInfo, - AssignmentDeclarationKind, - BinaryExpression, - BindingElement, - BindingName, - CallExpression, - canHaveDecorators, - canHaveModifiers, - canHaveSymbol, cast, - ClassDeclaration, - codefix, - combinePaths, concatenate, contains, - createModuleSpecifierResolutionHost, - createTextRangeFromSpan, - Debug, - Declaration, - DeclarationStatement, - Diagnostics, emptyArray, - EnumDeclaration, - escapeLeadingUnderscores, - Expression, - ExpressionStatement, - extensionFromPath, - ExternalModuleReference, - factory, - fileShouldUseJavaScriptRequire, find, - FindAllReferences, findIndex, firstDefined, flatMap, - forEachKey, - FunctionDeclaration, - getAssignmentDeclarationKind, GetCanonicalFileName, - getDecorators, - getDirectoryPath, - getLocaleSpecificMessage, - getModeForUsageLocation, - getModifiers, - getPropertySymbolFromBindingElement, - getQuotePreference, getRangesWhere, - getRefactorContextSpan, - getRelativePathFromFile, - getSynthesizedDeepClone, - getUniqueName, - hasSyntacticModifier, - hostGetCanonicalFileName, - Identifier, - ImportDeclaration, - ImportEqualsDeclaration, - insertImports, - InterfaceDeclaration, - InternalSymbolName, + last, + length, + mapDefined, + some, + takeWhile, + tryCast, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { extensionFromPath } from "../../compiler/extension"; +import { factory } from "../../compiler/factory/nodeFactory"; +import { isArrayLiteralExpression, isBinaryExpression, isBindingElement, - isDeclarationName, isExpressionStatement, isExternalModuleReference, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, - isNamedDeclaration, isObjectLiteralExpression, isOmittedExpression, - isPrologueDirective, isPropertyAccessExpression, isPropertyAssignment, - isRequireCall, isSourceFile, isStringLiteral, - isStringLiteralLike, - isValidTypeOnlyAliasUseSite, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, - LanguageServiceHost, - last, - length, - makeImportIfNecessary, - makeStringLiteral, - mapDefined, +} from "../../compiler/factory/nodeTests"; +import { + canHaveDecorators, + canHaveModifiers, +} from "../../compiler/factory/utilitiesPublic"; +import { getModuleSpecifier } from "../../compiler/moduleSpecifiers"; +import { + combinePaths, + getDirectoryPath, + getRelativePathFromFile, + normalizePath, + resolvePath, +} from "../../compiler/path"; +import { getModeForUsageLocation } from "../../compiler/program"; +import { + AnyImportOrRequireStatement, + AssignmentDeclarationKind, + BinaryExpression, + BindingElement, + BindingName, + CallExpression, + ClassDeclaration, + Declaration, + DeclarationStatement, + EnumDeclaration, + Expression, + ExpressionStatement, + ExternalModuleReference, + FunctionDeclaration, + Identifier, + ImportDeclaration, + ImportEqualsDeclaration, + InterfaceDeclaration, + InternalSymbolName, ModifierFlags, ModifierLike, ModuleDeclaration, NamedImportBindings, Node, NodeFlags, - nodeSeenTracker, - normalizePath, - ObjectBindingElementWithoutPropertyName, Program, PropertyAccessExpression, PropertyAssignment, - QuotePreference, - rangeContainsRange, - RefactorContext, - RefactorEditInfo, RequireOrImportCall, RequireVariableStatement, - resolvePath, ScriptTarget, - skipAlias, - some, SourceFile, Statement, StringLiteralLike, Symbol, SymbolFlags, - symbolNameNoDefault, SyntaxKind, - takeWhile, - textChanges, TransformFlags, - tryCast, TypeAliasDeclaration, TypeChecker, TypeNode, @@ -130,8 +99,59 @@ import { VariableDeclaration, VariableDeclarationList, VariableStatement, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + forEachKey, + getAssignmentDeclarationKind, + getLocaleSpecificMessage, + hasSyntacticModifier, + hostGetCanonicalFileName, + isDeclarationName, + isPrologueDirective, + isRequireCall, + isValidTypeOnlyAliasUseSite, + skipAlias, +} from "../../compiler/utilities"; +import { + canHaveSymbol, + escapeLeadingUnderscores, + getDecorators, + getModifiers, + isNamedDeclaration, + isStringLiteralLike, +} from "../../compiler/utilitiesPublic"; +import { + createImportAdder, + ImportAdder, + moduleSpecifierToValidIdentifier, +} from "../codefixes/importAdder"; +import { Core as FindAllReferences } from "../findAllReferences"; import { registerRefactor } from "../refactorProvider"; +import { ChangeTracker } from "../textChanges"; +import { + ApplicableRefactorInfo, + LanguageServiceHost, + RefactorContext, + RefactorEditInfo, +} from "../types"; +import { + createModuleSpecifierResolutionHost, + createTextRangeFromSpan, + fileShouldUseJavaScriptRequire, + getPropertySymbolFromBindingElement, + getQuotePreference, + getRefactorContextSpan, + getSynthesizedDeepClone, + getUniqueName, + insertImports, + makeImportIfNecessary, + makeStringLiteral, + nodeSeenTracker, + ObjectBindingElementWithoutPropertyName, + QuotePreference, + rangeContainsRange, + symbolNameNoDefault, +} from "../utilities"; const refactorNameForMoveToFile = "Move to file"; const description = getLocaleSpecificMessage(Diagnostics.Move_to_file); @@ -162,12 +182,12 @@ registerRefactor(refactorNameForMoveToFile, { Debug.assert(actionName === refactorNameForMoveToFile, "Wrong refactor invoked"); const statements = Debug.checkDefined(getStatementsToMove(context)); Debug.assert(interactiveRefactorArguments, "No interactive refactor arguments available"); - const edits = textChanges.ChangeTracker.with(context, t => doChange(context, context.file, interactiveRefactorArguments.targetFile, context.program, statements, t, context.host, context.preferences)); + const edits = ChangeTracker.with(context, t => doChange(context, context.file, interactiveRefactorArguments.targetFile, context.program, statements, t, context.host, context.preferences)); return { edits, renameFilename: undefined, renameLocation: undefined }; } }); -function doChange(context: RefactorContext, oldFile: SourceFile, targetFile: string, program: Program, toMove: ToMove, changes: textChanges.ChangeTracker, host: LanguageServiceHost, preferences: UserPreferences): void { +function doChange(context: RefactorContext, oldFile: SourceFile, targetFile: string, program: Program, toMove: ToMove, changes: ChangeTracker, host: LanguageServiceHost, preferences: UserPreferences): void { const checker = program.getTypeChecker(); const usage = getUsageInfo(oldFile, toMove.all, checker); //For a new file or an existing blank target file @@ -177,13 +197,13 @@ function doChange(context: RefactorContext, oldFile: SourceFile, targetFile: str } else { const targetSourceFile = Debug.checkDefined(program.getSourceFile(targetFile)); - const importAdder = codefix.createImportAdder(targetSourceFile, context.program, context.preferences, context.host); + const importAdder = createImportAdder(targetSourceFile, context.program, context.preferences, context.host, /*useAutoImportProvider*/ false); getNewStatementsAndRemoveFromOldFile(oldFile, targetSourceFile, usage, changes, toMove, program, host, preferences, importAdder); } } function getNewStatementsAndRemoveFromOldFile( - oldFile: SourceFile, targetFile: string | SourceFile, usage: UsageInfo, changes: textChanges.ChangeTracker, toMove: ToMove, program: Program, host: LanguageServiceHost, preferences: UserPreferences, importAdder?: codefix.ImportAdder + oldFile: SourceFile, targetFile: string | SourceFile, usage: UsageInfo, changes: ChangeTracker, toMove: ToMove, program: Program, host: LanguageServiceHost, preferences: UserPreferences, importAdder?: ImportAdder ) { const checker = program.getTypeChecker(); const prologueDirectives = takeWhile(oldFile.statements, isPrologueDirective); @@ -240,13 +260,13 @@ function getTargetFileImportsAndAddExportInOldFile( targetFile: string, importsToCopy: Map, targetFileImportsFromOldFile: Set, - changes: textChanges.ChangeTracker, + changes: ChangeTracker, checker: TypeChecker, program: Program, host: LanguageServiceHost, useEsModuleSyntax: boolean, quotePreference: QuotePreference, - importAdder?: codefix.ImportAdder, + importAdder?: ImportAdder, ): readonly AnyImportOrRequireStatement[] { const copiedOldImports: AnyImportOrRequireStatement[] = []; /** @@ -324,7 +344,7 @@ function getTargetFileImportsAndAddExportInOldFile( } /** @internal */ -export function addNewFileToTsconfig(program: Program, changes: textChanges.ChangeTracker, oldFileName: string, newFileNameWithExtension: string, getCanonicalFileName: GetCanonicalFileName): void { +export function addNewFileToTsconfig(program: Program, changes: ChangeTracker, oldFileName: string, newFileNameWithExtension: string, getCanonicalFileName: GetCanonicalFileName): void { const cfg = program.getCompilerOptions().configFile; if (!cfg) return; @@ -340,14 +360,14 @@ export function addNewFileToTsconfig(program: Program, changes: textChanges.Chan } /** @internal */ -export function deleteMovedStatements(sourceFile: SourceFile, moved: readonly StatementRange[], changes: textChanges.ChangeTracker) { +export function deleteMovedStatements(sourceFile: SourceFile, moved: readonly StatementRange[], changes: ChangeTracker) { for (const { first, afterLast } of moved) { changes.deleteNodeRangeExcludingEnd(sourceFile, first, afterLast); } } /** @internal */ -export function deleteUnusedOldImports(oldFile: SourceFile, toMove: readonly Statement[], changes: textChanges.ChangeTracker, toDelete: Set, checker: TypeChecker) { +export function deleteUnusedOldImports(oldFile: SourceFile, toMove: readonly Statement[], changes: ChangeTracker, toDelete: Set, checker: TypeChecker) { for (const statement of oldFile.statements) { if (contains(toMove, statement)) continue; forEachImportInStatement(statement, i => deleteUnusedImports(oldFile, i, changes, name => toDelete.has(checker.getSymbolAtLocation(name)!))); @@ -356,7 +376,7 @@ export function deleteUnusedOldImports(oldFile: SourceFile, toMove: readonly Sta /** @internal */ export function updateImportsInOtherFiles( - changes: textChanges.ChangeTracker, program: Program, host: LanguageServiceHost, oldFile: SourceFile, movedSymbols: Set, targetFileName: string, quotePreference: QuotePreference + changes: ChangeTracker, program: Program, host: LanguageServiceHost, oldFile: SourceFile, movedSymbols: Set, targetFileName: string, quotePreference: QuotePreference ): void { const checker = program.getTypeChecker(); for (const sourceFile of program.getSourceFiles()) { @@ -400,7 +420,7 @@ function getNamespaceLikeImport(node: SupportedImport): Identifier | undefined { } function updateNamespaceLikeImport( - changes: textChanges.ChangeTracker, + changes: ChangeTracker, sourceFile: SourceFile, checker: TypeChecker, movedSymbols: Set, @@ -409,10 +429,10 @@ function updateNamespaceLikeImport( oldImportNode: SupportedImport, quotePreference: QuotePreference ): void { - const preferredNewNamespaceName = codefix.moduleSpecifierToValidIdentifier(newModuleSpecifier, ScriptTarget.ESNext); + const preferredNewNamespaceName = moduleSpecifierToValidIdentifier(newModuleSpecifier, ScriptTarget.ESNext); let needUniqueName = false; const toChange: Identifier[] = []; - FindAllReferences.Core.eachSymbolReferenceInFile(oldImportId, checker, sourceFile, ref => { + FindAllReferences.eachSymbolReferenceInFile(oldImportId, checker, sourceFile, ref => { if (!isPropertyAccessExpression(ref.parent)) return; needUniqueName = needUniqueName || !!checker.resolveName(preferredNewNamespaceName, ref, SymbolFlags.All, /*excludeGlobals*/ true); if (movedSymbols.has(checker.getSymbolAtLocation(ref.parent.name)!)) { @@ -566,7 +586,7 @@ function isExported(sourceFile: SourceFile, decl: TopLevelDeclarationStatement, } /** @internal */ -export function deleteUnusedImports(sourceFile: SourceFile, importDecl: SupportedImport, changes: textChanges.ChangeTracker, isUnused: (name: Identifier) => boolean): void { +export function deleteUnusedImports(sourceFile: SourceFile, importDecl: SupportedImport, changes: ChangeTracker, isUnused: (name: Identifier) => boolean): void { switch (importDecl.kind) { case SyntaxKind.ImportDeclaration: deleteUnusedImportsInDeclaration(sourceFile, importDecl, changes, isUnused); @@ -584,7 +604,7 @@ export function deleteUnusedImports(sourceFile: SourceFile, importDecl: Supporte } } -function deleteUnusedImportsInDeclaration(sourceFile: SourceFile, importDecl: ImportDeclaration, changes: textChanges.ChangeTracker, isUnused: (name: Identifier) => boolean): void { +function deleteUnusedImportsInDeclaration(sourceFile: SourceFile, importDecl: ImportDeclaration, changes: ChangeTracker, isUnused: (name: Identifier) => boolean): void { if (!importDecl.importClause) return; const { name, namedBindings } = importDecl.importClause; const defaultUnused = !name || isUnused(name); @@ -614,7 +634,7 @@ function deleteUnusedImportsInDeclaration(sourceFile: SourceFile, importDecl: Im } } -function deleteUnusedImportsInVariableDeclaration(sourceFile: SourceFile, varDecl: VariableDeclaration, changes: textChanges.ChangeTracker, isUnused: (name: Identifier) => boolean) { +function deleteUnusedImportsInVariableDeclaration(sourceFile: SourceFile, varDecl: VariableDeclaration, changes: ChangeTracker, isUnused: (name: Identifier) => boolean) { const { name } = varDecl; switch (name.kind) { case SyntaxKind.Identifier: @@ -785,7 +805,7 @@ export function getTopLevelDeclarationStatement(d: TopLevelDeclaration): TopLeve } /** @internal */ -export function addExportToChanges(sourceFile: SourceFile, decl: TopLevelDeclarationStatement, name: Identifier, changes: textChanges.ChangeTracker, useEs6Exports: boolean): void { +export function addExportToChanges(sourceFile: SourceFile, decl: TopLevelDeclarationStatement, name: Identifier, changes: ChangeTracker, useEs6Exports: boolean): void { if (isExported(sourceFile, decl, useEs6Exports, name)) return; if (useEs6Exports) { if (!isExpressionStatement(decl)) changes.insertExportModifier(sourceFile, decl); diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index 10183ad20acc4..b70cc16f0d466 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -1,33 +1,44 @@ import { append, - ApplicableRefactorInfo, - Debug, - Diagnostics, emptyArray, - fileShouldUseJavaScriptRequire, + takeWhile, +} from "../../compiler/core"; +import * as Debug from "../../compiler/debug"; +import { Diagnostics } from "../../compiler/diagnosticInformationMap.generated"; +import { getBaseFileName, - getLocaleSpecificMessage, - getQuotePreference, - hasSyntacticModifier, - hostGetCanonicalFileName, +} from "../../compiler/path"; +import { Identifier, - insertImports, - isPrologueDirective, - LanguageServiceHost, ModifierFlags, - nodeSeenTracker, Program, - QuotePreference, - RefactorContext, - RefactorEditInfo, SourceFile, Symbol, SyntaxKind, - takeWhile, - textChanges, TypeChecker, UserPreferences, -} from "../_namespaces/ts"; +} from "../../compiler/types"; +import { + getLocaleSpecificMessage, + hasSyntacticModifier, + hostGetCanonicalFileName, + isPrologueDirective, +} from "../../compiler/utilities"; +import { registerRefactor } from "../refactorProvider"; +import { ChangeTracker } from "../textChanges"; +import { + ApplicableRefactorInfo, + LanguageServiceHost, + RefactorContext, + RefactorEditInfo, +} from "../types"; +import { + fileShouldUseJavaScriptRequire, + getQuotePreference, + insertImports, + nodeSeenTracker, + QuotePreference, +} from "../utilities"; import { addExports, addExportToChanges, @@ -45,12 +56,11 @@ import { makeImportOrRequire, moduleSpecifierFromImport, nameOfTopLevelDeclaration, - registerRefactor, SupportedImportStatement, ToMove, updateImportsInOtherFiles, - UsageInfo -} from "../_namespaces/ts.refactor"; + UsageInfo, +} from "./moveToFile"; const refactorName = "Move to a new file"; const description = getLocaleSpecificMessage(Diagnostics.Move_to_a_new_file); @@ -77,12 +87,12 @@ registerRefactor(refactorName, { getEditsForAction: function getRefactorEditsToMoveToNewFile(context, actionName): RefactorEditInfo { Debug.assert(actionName === refactorName, "Wrong refactor invoked"); const statements = Debug.checkDefined(getStatementsToMove(context)); - const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, statements, t, context.host, context.preferences, context)); + const edits = ChangeTracker.with(context, t => doChange(context.file, context.program, statements, t, context.host, context.preferences, context)); return { edits, renameFilename: undefined, renameLocation: undefined }; } }); -function doChange(oldFile: SourceFile, program: Program, toMove: ToMove, changes: textChanges.ChangeTracker, host: LanguageServiceHost, preferences: UserPreferences, context: RefactorContext): void { +function doChange(oldFile: SourceFile, program: Program, toMove: ToMove, changes: ChangeTracker, host: LanguageServiceHost, preferences: UserPreferences, context: RefactorContext): void { const checker = program.getTypeChecker(); const usage = getUsageInfo(oldFile, toMove.all, checker); @@ -95,7 +105,7 @@ function doChange(oldFile: SourceFile, program: Program, toMove: ToMove, changes } function getNewStatementsAndRemoveFromOldFile( - oldFile: SourceFile, usage: UsageInfo, changes: textChanges.ChangeTracker, toMove: ToMove, program: Program, host: LanguageServiceHost, newFilename: string, preferences: UserPreferences, + oldFile: SourceFile, usage: UsageInfo, changes: ChangeTracker, toMove: ToMove, program: Program, host: LanguageServiceHost, newFilename: string, preferences: UserPreferences, ) { const checker = program.getTypeChecker(); const prologueDirectives = takeWhile(oldFile.statements, isPrologueDirective); @@ -137,7 +147,7 @@ function getNewFileImportsAndAddExportInOldFile( oldFile: SourceFile, importsToCopy: Map, newFileImportsFromOldFile: Set, - changes: textChanges.ChangeTracker, + changes: ChangeTracker, checker: TypeChecker, program: Program, host: LanguageServiceHost, diff --git a/src/services/rename.ts b/src/services/rename.ts index 094e22b15a0f9..c6e3d2a014894 100644 --- a/src/services/rename.ts +++ b/src/services/rename.ts @@ -1,57 +1,74 @@ import { compareStringsCaseSensitive, - Comparison, - createTextSpan, - DiagnosticMessage, - Diagnostics, endsWith, every, - Extension, - fileExtensionIs, find, - getAdjustedRenameLocation, - getContextualTypeFromParentOrAncestorTypeNode, - getLocaleSpecificMessage, - getPathComponents, - getTextOfIdentifierOrLiteral, - getTextOfNode, - getTouchingPropertyName, - ImportSpecifier, - isExternalModuleNameRelative, + some, + tryRemoveSuffix, +} from "../compiler/core"; +import { Comparison } from "../compiler/corePublic"; +import { Diagnostics } from "../compiler/diagnosticInformationMap.generated"; +import { removeFileExtension } from "../compiler/extension"; +import { isIdentifier, - isImportOrExportSpecifierName, isImportSpecifier, - isInsideNodeModules, - isLabelName, - isLiteralNameOfPropertyDeclarationOrIndexAccess, isSourceFile, - isStringLiteralLike, - isStringOrNumericLiteralLike, +} from "../compiler/factory/nodeTests"; +import { + fileExtensionIs, + getPathComponents, +} from "../compiler/path"; +import { + DiagnosticMessage, + Extension, + ImportSpecifier, Node, NumericLiteral, Path, Program, - removeFileExtension, - RenameInfo, - RenameInfoFailure, - RenameInfoSuccess, - ScriptElementKind, - ScriptElementKindModifier, - some, SourceFile, StringLiteralLike, - stripQuotes, Symbol, - SymbolDisplay, SymbolFlags, SyntaxKind, - tryGetImportFromModuleSpecifier, - tryRemoveSuffix, TypeChecker, TypeFlags, UnionType, UserPreferences, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + getLocaleSpecificMessage, + getTextOfIdentifierOrLiteral, + getTextOfNode, + isStringOrNumericLiteralLike, + stripQuotes, + tryGetImportFromModuleSpecifier, +} from "../compiler/utilities"; +import { + createTextSpan, + isExternalModuleNameRelative, + isStringLiteralLike, +} from "../compiler/utilitiesPublic"; +import { + getSymbolKind, + getSymbolModifiers, +} from "./symbolDisplay"; +import { + RenameInfo, + RenameInfoFailure, + RenameInfoSuccess, + ScriptElementKind, + ScriptElementKindModifier, +} from "./types"; +import { + getAdjustedRenameLocation, + getContextualTypeFromParentOrAncestorTypeNode, + getTouchingPropertyName, + isImportOrExportSpecifierName, + isInsideNodeModules, + isLabelName, + isLiteralNameOfPropertyDeclarationOrIndexAccess, +} from "./utilities"; /** @internal */ export function getRenameInfo(program: Program, sourceFile: SourceFile, position: number, preferences: UserPreferences): RenameInfo { @@ -111,13 +128,13 @@ function getRenameInfoForNode( return getRenameInfoError(wouldRenameNodeModules); } - const kind = SymbolDisplay.getSymbolKind(typeChecker, symbol, node); + const kind = getSymbolKind(typeChecker, symbol, node); const specifierName = (isImportOrExportSpecifierName(node) || isStringOrNumericLiteralLike(node) && node.parent.kind === SyntaxKind.ComputedPropertyName) ? stripQuotes(getTextOfIdentifierOrLiteral(node)) : undefined; const displayName = specifierName || typeChecker.symbolToString(symbol); const fullDisplayName = specifierName || typeChecker.getFullyQualifiedName(symbol); - return getRenameInfoSuccess(displayName, fullDisplayName, kind, SymbolDisplay.getSymbolModifiers(typeChecker,symbol), node, sourceFile); + return getRenameInfoSuccess(displayName, fullDisplayName, kind, getSymbolModifiers(typeChecker,symbol), node, sourceFile); } function isDefinedInLibraryFile(program: Program, declaration: Node) { diff --git a/src/services/services.ts b/src/services/services.ts index f0518d0b1ee67..a830208438872 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1,162 +1,46 @@ +import { getFileEmitOutput } from "../compiler/builderState"; +import { + ParseConfigFileHost, + parseJsonSourceFileConfigFileContent, +} from "../compiler/commandLineParser"; import { - __String, - ApplicableRefactorInfo, - ApplyCodeActionCommandResult, - AssignmentDeclarationKind, - BaseType, - BinaryExpression, - BreakpointResolver, - CallHierarchy, - CallHierarchyIncomingCall, - CallHierarchyItem, - CallHierarchyOutgoingCall, - CancellationToken, - changeCompilerHostLikeToUseCache, - CharacterCodes, - CheckJsDirective, - Classifications, - ClassifiedSpan, - ClassifiedSpan2020, - CodeActionCommand, - codefix, - CodeFixAction, - CombinedCodeActions, - CombinedCodeFixScope, - combinePaths, compareValues, - CompilerHost, - CompilerOptions, - CompletionEntryData, - CompletionEntryDetails, - CompletionInfo, - Completions, - computePositionOfLineAndCharacter, - computeSuggestionDiagnostics, - containsParseError, - createDocumentRegistry, createGetCanonicalFileName, createMultiMap, - createProgram, - CreateProgramOptions, - createSourceFile, - CreateSourceFileOptions, - createTextSpanFromBounds, - createTextSpanFromNode, - createTextSpanFromRange, - Debug, - Declaration, deduplicate, - DefinitionInfo, - DefinitionInfoAndBoundSpan, - Diagnostic, - DiagnosticWithLocation, - directoryProbablyExists, - DocCommentTemplateOptions, - DocumentHighlights, - DocumentRegistry, - DocumentSpan, - EditorOptions, - EditorSettings, - ElementAccessExpression, - EmitTextWriter, emptyArray, - emptyOptions, - EndOfFileToken, - EntityName, equateValues, - ExportDeclaration, - Extension, - extensionFromPath, - FileReference, - FileTextChanges, filter, find, - FindAllReferences, - findAncestor, - findChildOfKind, - findPrecedingToken, first, firstDefined, - firstOrOnly, flatMap, forEach, - forEachChild, - FormatCodeOptions, - FormatCodeSettings, - formatting, - FunctionLikeDeclaration, - getAdjustedRenameLocation, - getAllSuperTypeNodes, - getAssignmentDeclarationKind, - getBaseFileName, - GetCompletionsAtPositionOptions, - getContainerNode, - getDefaultLibFileName, - getDirectoryPath, - getEditsForFileRename as ts_getEditsForFileRename, - getEmitDeclarations, - getEscapedTextOfIdentifierOrLiteral, - getFileEmitOutput, - getImpliedNodeFormatForFile, - getJSDocTags, - getLineAndCharacterOfPosition, - getLineStarts, - getMappedDocumentSpan, - getNameFromPropertyName, - getNewLineCharacter, - getNewLineOrDefaultFromHost, - getNonAssignedNameOfDeclaration, - getNormalizedAbsolutePath, - getObjectFlags, - getQuotePreference, - getScriptKind, - getSetExternalModuleIndicator, - getSnapshotText, - getSourceFileOfNode, - getSourceMapper, - getTokenPosOfNode, - getTouchingPropertyName, - getTouchingToken, - GoToDefinition, - HasInvalidatedResolutions, - hasJSDocNodes, hasProperty, - hasStaticModifier, - hasSyntacticModifier, - hasTabstop, - HostCancellationToken, - hostGetCanonicalFileName, - hostUsesCaseSensitiveFileNames, - Identifier, identity, - idText, - ImplementationLocation, - ImportDeclaration, - IndexKind, - IndexType, - InlayHint, - InlayHints, - InlayHintsContext, insertSorted, - InteractiveRefactorArguments, - InterfaceType, - IntersectionType, isArray, - isBindingPattern, + lastOrUndefined, + length, + map, + mapDefined, + maybeBind, + noop, + returnFalse, + singleElementArray, + startsWith, + stringContains, +} from "../compiler/core"; +import { + MapLike, + SortedArray, +} from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { extensionFromPath } from "../compiler/extension"; +import { getSnippetElement } from "../compiler/factory/emitNode"; +import { isComputedPropertyName, - isConstTypeReference, - IScriptSnapshot, - isDeclarationName, - isGetAccessor, isIdentifier, - isImportMeta, - isInComment, - isInsideJsxElement, - isInsideJsxElementOrAttribute, - isInString, - isInTemplateString, - isIntrinsicJsxName, - isJSDocCommentContainingNode, isJsxAttributes, isJsxClosingElement, isJsxElement, @@ -165,149 +49,131 @@ import { isJsxOpeningElement, isJsxOpeningFragment, isJsxText, - isLabelName, - isLiteralComputedPropertyDeclarationName, isNamedExports, isNamedTupleMember, - isNameOfModuleDeclaration, isNewExpression, - isNodeKind, - isObjectLiteralElement, isObjectLiteralExpression, isPrivateIdentifier, - isProgramUptoDate, isPropertyAccessExpression, - isPropertyName, - isRightSideOfPropertyAccess, - isRightSideOfQualifiedName, - isSetAccessor, - isStringOrNumericLiteralLike, - isTagName, - isTextWhiteSpaceLike, - isThisTypeParameter, - isTransientSymbol, +} from "../compiler/factory/nodeTests"; +import { ModeAwareCache } from "../compiler/moduleNameResolver"; +import { + ObjectAllocator, + setObjectAllocator, +} from "../compiler/objectAllocator"; +import { + createSourceFile, + CreateSourceFileOptions, + forEachChild, + tagNamesAreEquivalent, + updateSourceFile, +} from "../compiler/parser"; +import { containsParseError } from "../compiler/parserUtilities"; +import { + combinePaths, + getBaseFileName, + getDirectoryPath, + getNormalizedAbsolutePath, + normalizePath, + toPath, +} from "../compiler/path"; +import { timestamp } from "../compiler/performanceCore"; +import { + changeCompilerHostLikeToUseCache, + createProgram, + getImpliedNodeFormatForFile, + getSetExternalModuleIndicator, + isProgramUptoDate, +} from "../compiler/program"; +import { + computePositionOfLineAndCharacter, + getLineAndCharacterOfPosition, + getLineStarts, +} from "../compiler/scanner"; +import { positionIsSynthesized } from "../compiler/scannerUtilities"; +import { sys } from "../compiler/sys"; +import { tracing } from "../compiler/tracing"; +import { + __String, + AssignmentDeclarationKind, + BaseType, + BinaryExpression, + CancellationToken, + CharacterCodes, + CheckJsDirective, + CompilerHost, + CompilerOptions, + CreateProgramOptions, + Declaration, + Diagnostic, + DiagnosticWithLocation, + ElementAccessExpression, + EmitTextWriter, + EndOfFileToken, + EntityName, + ExportDeclaration, + Extension, + FileReference, + FunctionLikeDeclaration, + HasInvalidatedResolutions, + Identifier, + ImportDeclaration, + IndexKind, + IndexType, + InterfaceType, + IntersectionType, JSDoc, - JsDoc, JSDocContainer, - JSDocTagInfo, JsonSourceFile, JsxAttributes, - JsxClosingTagInfo, JsxElement, JsxEmit, JsxFragment, - LanguageService, - LanguageServiceHost, - LanguageServiceMode, LanguageVariant, - lastOrUndefined, - length, LineAndCharacter, - lineBreakPart, - LinkedEditingInfo, LiteralType, - map, - mapDefined, - MapLike, - mapOneOrMany, - maybeBind, - maybeSetLocalizedDiagnosticMessages, - ModeAwareCache, ModifierFlags, ModuleDeclaration, - NavigateToItem, - NavigationBarItem, - NavigationTree, Node, NodeArray, NodeFlags, - noop, - normalizePath, NumberLiteralType, NumericLiteral, - ObjectAllocator, ObjectFlags, ObjectLiteralElement, ObjectLiteralExpression, OperationCanceledException, - OrganizeImports, - OrganizeImportsArgs, - OrganizeImportsMode, - OutliningElementsCollector, - OutliningSpan, - ParseConfigFileHost, ParsedCommandLine, - parseJsonSourceFileConfigFileContent, Path, - positionIsSynthesized, - PossibleProgramFileInfo, PragmaMap, PrivateIdentifier, Program, PropertyName, - QuickInfo, - refactor, - RefactorContext, - RefactorEditInfo, - RefactorTriggerReason, - ReferencedSymbol, - ReferenceEntry, - Rename, - RenameInfo, - RenameInfoOptions, - RenameLocation, ResolvedModuleWithFailedLookupLocations, ResolvedProjectReference, ResolvedTypeReferenceDirectiveWithFailedLookupLocations, - returnFalse, - scanner, - ScriptElementKind, - ScriptElementKindModifier, ScriptKind, ScriptTarget, - SelectionRange, - SemanticClassificationFormat, - setObjectAllocator, Signature, SignatureDeclaration, SignatureFlags, - SignatureHelp, - SignatureHelpItems, - SignatureHelpItemsOptions, SignatureKind, - singleElementArray, - SmartSelectionRange, - SortedArray, + SnippetKind, SourceFile, SourceFileLike, SourceMapSource, - startsWith, Statement, - stringContains, StringLiteral, StringLiteralLike, StringLiteralType, Symbol, - SymbolDisplay, - SymbolDisplayPart, SymbolFlags, - symbolName, SyntaxKind, SyntaxList, - sys, - tagNamesAreEquivalent, - TextChange, TextChangeRange, - TextInsertion, TextRange, TextSpan, - textSpanEnd, - timestamp, - TodoComment, - TodoCommentDescriptor, Token, - toPath, - tracing, TransformFlags, Type, TypeChecker, @@ -316,18 +182,198 @@ import { TypeParameter, TypePredicate, TypeReference, - typeToDisplayParts, UnionOrIntersectionType, UnionType, - updateSourceFile, UserPreferences, VariableDeclaration, -} from "./_namespaces/ts"; -import * as NavigateTo from "./_namespaces/ts.NavigateTo"; -import * as NavigationBar from "./_namespaces/ts.NavigationBar"; -import { createNewFileName } from "./_namespaces/ts.refactor"; +} from "../compiler/types"; +import { + directoryProbablyExists, + getAllSuperTypeNodes, + getAssignmentDeclarationKind, + getEmitDeclarations, + getEscapedTextOfIdentifierOrLiteral, + getNewLineCharacter, + getObjectFlags, + getSourceFileOfNode, + getTokenPosOfNode, + hasStaticModifier, + hasSyntacticModifier, + hostGetCanonicalFileName, + hostUsesCaseSensitiveFileNames, + isDeclarationName, + isImportMeta, + isIntrinsicJsxName, + isLiteralComputedPropertyDeclarationName, + isStringOrNumericLiteralLike, + isThisTypeParameter, + isTransientSymbol, + maybeSetLocalizedDiagnosticMessages, +} from "../compiler/utilities"; +import { + createTextSpanFromBounds, + findAncestor, + getDefaultLibFileName, + getJSDocTags, + getNonAssignedNameOfDeclaration, + hasJSDocNodes, + idText, + isBindingPattern, + isConstTypeReference, + isGetAccessor, + isJSDocCommentContainingNode, + isNodeKind, + isObjectLiteralElement, + isPropertyName, + isSetAccessor, + symbolName, + textSpanEnd, +} from "../compiler/utilitiesPublic"; +import { spanInSourceFileAtLocation } from "./breakpoints"; +import { + createCallHierarchyItem, + getIncomingCalls, + getOutgoingCalls, + resolveCallHierarchyDeclaration, +} from "./callHierarchy"; import * as classifier from "./classifier"; import * as classifier2020 from "./classifier2020"; +import { + getAllFixes, + getFixes, + getSupportedErrorCodes, +} from "./codeFixProvider"; +import * as completions from "./completions"; +import * as documentHighlights from "./documentHighlights"; +import { + createDocumentRegistry, + DocumentRegistry, +} from "./documentRegistry"; +import * as FindAllReferences from "./findAllReferences"; +import * as formatting from "./formatting/formatting"; +import { getFormatContext } from "./formatting/formatting"; +import { SmartIndenter } from "./formatting/smartIndenter"; +import * as EditsForFileRename from "./getEditsForFileRename"; +import * as GoToDefinition from "./goToDefinition"; +import * as InlayHints from "./inlayHints"; +import * as JsDoc from "./jsDoc"; +import * as NavigateTo from "./navigateTo"; +import * as NavigationBar from "./navigationBar"; +import * as OrganizeImports from "./organizeImports"; +import * as OutliningElementsCollector from "./outliningElementsCollector"; +import * as refactor from "./refactorProvider"; +import { createNewFileName } from "./refactors/moveToFile"; +import * as Rename from "./rename"; +import * as SignatureHelp from "./signatureHelp"; +import * as SmartSelectionRange from "./smartSelection"; +import { getSourceMapper } from "./sourcemaps"; +import { computeSuggestionDiagnostics } from "./suggestionDiagnostics"; +import { + getSymbolDisplayPartsDocumentationAndSymbolKind, + getSymbolModifiers, +} from "./symbolDisplay"; +import { + ApplicableRefactorInfo, + ApplyCodeActionCommandResult, + CallHierarchyIncomingCall, + CallHierarchyItem, + CallHierarchyOutgoingCall, + Classifications, + ClassifiedSpan, + ClassifiedSpan2020, + CodeActionCommand, + CodeFixAction, + CombinedCodeActions, + CombinedCodeFixScope, + CompletionEntryData, + CompletionEntryDetails, + CompletionInfo, + DefinitionInfo, + DefinitionInfoAndBoundSpan, + DocCommentTemplateOptions, + DocumentHighlights, + DocumentSpan, + EditorOptions, + EditorSettings, + emptyOptions, + FileTextChanges, + FormatCodeOptions, + FormatCodeSettings, + GetCompletionsAtPositionOptions, + HostCancellationToken, + ImplementationLocation, + InlayHint, + InlayHintsContext, + InteractiveRefactorArguments, + IScriptSnapshot, + JSDocTagInfo, + JsxClosingTagInfo, + LanguageService, + LanguageServiceHost, + LanguageServiceMode, + LinkedEditingInfo, + NavigateToItem, + NavigationBarItem, + NavigationTree, + OrganizeImportsArgs, + OrganizeImportsMode, + OutliningSpan, + QuickInfo, + RefactorContext, + RefactorEditInfo, + RefactorTriggerReason, + ReferencedSymbol, + ReferenceEntry, + RenameInfo, + RenameInfoOptions, + RenameLocation, + ScriptElementKind, + ScriptElementKindModifier, + SelectionRange, + SemanticClassificationFormat, + SignatureHelpItems, + SignatureHelpItemsOptions, + SymbolDisplayPart, + TextChange, + TextInsertion, + TodoComment, + TodoCommentDescriptor, +} from "./types"; +import { + createTextSpanFromNode, + createTextSpanFromRange, + findChildOfKind, + findPrecedingToken, + firstOrOnly, + getAdjustedRenameLocation, + getContainerNode, + getMappedDocumentSpan, + getNameFromPropertyName, + getNewLineOrDefaultFromHost, + getQuotePreference, + getRangeOfEnclosingComment, + getScriptKind, + getSnapshotText, + getTouchingPropertyName, + getTouchingToken, + isInComment, + isInsideJsxElement, + isInsideJsxElementOrAttribute, + isInString, + isInTemplateString, + isLabelName, + isNameOfModuleDeclaration, + isRightSideOfPropertyAccess, + isRightSideOfQualifiedName, + isTagName, + isTextWhiteSpaceLike, + lineBreakPart, + mapOneOrMany, + PossibleProgramFileInfo, + scanner, + toContextSpan, + typeToDisplayParts, +} from "./utilities"; /** The version of the language service API */ export const servicesVersion = "0.8"; @@ -520,6 +566,10 @@ function addSyntheticNodes(nodes: Node[], pos: number, end: number, parent: Node } } +function hasTabstop(node: Node): boolean { + return getSnippetElement(node)?.kind === SnippetKind.TabStop; +} + function createSyntaxList(nodes: NodeArray, parent: Node): Node { const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, parent) as any as SyntaxList; list._children = []; @@ -1324,7 +1374,7 @@ export function getDefaultCompilerOptions(): CompilerOptions { } export function getSupportedCodeFixes() { - return codefix.getSupportedErrorCodes(); + return getSupportedErrorCodes(); } class SyntaxTreeCache { @@ -1992,7 +2042,7 @@ export function createLanguageService( includeCompletionsWithInsertText: options.includeCompletionsWithInsertText || options.includeInsertTextCompletions, }; synchronizeHostData(); - return Completions.getCompletionsAtPosition( + return completions.getCompletionsAtPosition( host, program, log, @@ -2002,20 +2052,20 @@ export function createLanguageService( options.triggerCharacter, options.triggerKind, cancellationToken, - formattingSettings && formatting.getFormatContext(formattingSettings, host), + formattingSettings && getFormatContext(formattingSettings, host), options.includeSymbol); } function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions: FormatCodeSettings | undefined, source: string | undefined, preferences: UserPreferences = emptyOptions, data?: CompletionEntryData): CompletionEntryDetails | undefined { synchronizeHostData(); - return Completions.getCompletionEntryDetails( + return completions.getCompletionEntryDetails( program, log, getValidSourceFile(fileName), position, { name, source, data }, host, - (formattingOptions && formatting.getFormatContext(formattingOptions, host))!, // TODO: GH#18217 + (formattingOptions && getFormatContext(formattingOptions, host))!, // TODO: GH#18217 preferences, cancellationToken, ); @@ -2023,7 +2073,7 @@ export function createLanguageService( function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string, preferences: UserPreferences = emptyOptions): Symbol | undefined { synchronizeHostData(); - return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host, preferences); + return completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host, preferences); } function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined { @@ -2053,11 +2103,11 @@ export function createLanguageService( } const { symbolKind, displayParts, documentation, tags } = typeChecker.runWithCancellationToken(cancellationToken, typeChecker => - SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, getContainerNode(nodeForQuickInfo), nodeForQuickInfo) + getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, getContainerNode(nodeForQuickInfo), nodeForQuickInfo) ); return { kind: symbolKind, - kindModifiers: SymbolDisplay.getSymbolModifiers(typeChecker, symbol), + kindModifiers: getSymbolModifiers(typeChecker, symbol), textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile), displayParts, documentation, @@ -2132,7 +2182,7 @@ export function createLanguageService( synchronizeHostData(); const sourceFilesToSearch = mapDefined(filesToSearch, fileName => program.getSourceFile(fileName)); const sourceFile = getValidSourceFile(fileName); - return DocumentHighlights.getDocumentHighlights(program, cancellationToken, sourceFile, position, sourceFilesToSearch); + return documentHighlights.getDocumentHighlights(program, cancellationToken, sourceFile, position, sourceFilesToSearch); } function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, preferences?: UserPreferences | boolean): RenameLocation[] | undefined { @@ -2147,7 +2197,7 @@ export function createLanguageService( return { fileName: sourceFile.fileName, textSpan, - ...FindAllReferences.toContextSpan(textSpan, sourceFile, node.parent) + ...toContextSpan(textSpan, sourceFile, node.parent) }; }); } @@ -2276,8 +2326,7 @@ export function createLanguageService( function getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan | undefined { // doesn't use compiler - no need to synchronize with host const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - - return BreakpointResolver.spanInSourceFileAtLocation(sourceFile, position); + return spanInSourceFileAtLocation(sourceFile, position); } function getNavigationBarItems(fileName: string): NavigationBarItem[] { @@ -2354,7 +2403,7 @@ export function createLanguageService( start = timestamp(); - const result = formatting.SmartIndenter.getIndentation(position, sourceFile, settings); + const result = SmartIndenter.getIndentation(position, sourceFile, settings); log("getIndentationAtPosition: computeIndentation : " + (timestamp() - start)); return result; @@ -2362,16 +2411,16 @@ export function createLanguageService( function getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions | FormatCodeSettings): TextChange[] { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - return formatting.formatSelection(start, end, sourceFile, formatting.getFormatContext(toEditorSettings(options), host)); + return formatting.formatSelection(start, end, sourceFile, getFormatContext(toEditorSettings(options), host)); } function getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] { - return formatting.formatDocument(syntaxTreeCache.getCurrentSourceFile(fileName), formatting.getFormatContext(toEditorSettings(options), host)); + return formatting.formatDocument(syntaxTreeCache.getCurrentSourceFile(fileName), getFormatContext(toEditorSettings(options), host)); } function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - const formatContext = formatting.getFormatContext(toEditorSettings(options), host); + const formatContext = getFormatContext(toEditorSettings(options), host); if (!isInComment(sourceFile, position)) { switch (key) { @@ -2393,11 +2442,11 @@ export function createLanguageService( synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); const span = createTextSpanFromBounds(start, end); - const formatContext = formatting.getFormatContext(formatOptions, host); + const formatContext = getFormatContext(formatOptions, host); return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => { cancellationToken.throwIfCancellationRequested(); - return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext, preferences }); + return getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext, preferences }); }); } @@ -2405,23 +2454,23 @@ export function createLanguageService( synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); - const formatContext = formatting.getFormatContext(formatOptions, host); + const formatContext = getFormatContext(formatOptions, host); - return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, preferences }); + return getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, preferences }); } function organizeImports(args: OrganizeImportsArgs, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): readonly FileTextChanges[] { synchronizeHostData(); Debug.assert(args.type === "file"); const sourceFile = getValidSourceFile(args.fileName); - const formatContext = formatting.getFormatContext(formatOptions, host); + const formatContext = getFormatContext(formatOptions, host); const mode = args.mode ?? (args.skipDestructiveCodeActions ? OrganizeImportsMode.SortAndCombine : OrganizeImportsMode.All); return OrganizeImports.organizeImports(sourceFile, formatContext, host, program, preferences, mode); } function getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): readonly FileTextChanges[] { - return ts_getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, formatting.getFormatContext(formatOptions, host), preferences, sourceMapper); + return EditsForFileRename.getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, getFormatContext(formatOptions, host), preferences, sourceMapper); } function applyCodeActionCommand(action: CodeActionCommand, formatSettings?: FormatCodeSettings): Promise; @@ -2803,7 +2852,7 @@ export function createLanguageService( function getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - const range = formatting.getRangeOfEnclosingComment(sourceFile, position); + const range = getRangeOfEnclosingComment(sourceFile, position); return range && (!onlyMultiLine || range.kind === SyntaxKind.MultiLineCommentTrivia) ? createTextSpanFromRange(range) : undefined; } @@ -2967,7 +3016,7 @@ export function createLanguageService( endPosition, program: getProgram()!, host, - formatContext: formatting.getFormatContext(formatOptions!, host), // TODO: GH#18217 + formatContext: getFormatContext(formatOptions!, host), // TODO: GH#18217 cancellationToken, preferences, triggerReason, @@ -3036,22 +3085,22 @@ export function createLanguageService( function prepareCallHierarchy(fileName: string, position: number): CallHierarchyItem | CallHierarchyItem[] | undefined { synchronizeHostData(); - const declarations = CallHierarchy.resolveCallHierarchyDeclaration(program, getTouchingPropertyName(getValidSourceFile(fileName), position)); - return declarations && mapOneOrMany(declarations, declaration => CallHierarchy.createCallHierarchyItem(program, declaration)); + const declarations = resolveCallHierarchyDeclaration(program, getTouchingPropertyName(getValidSourceFile(fileName), position)); + return declarations && mapOneOrMany(declarations, declaration => createCallHierarchyItem(program, declaration)); } function provideCallHierarchyIncomingCalls(fileName: string, position: number): CallHierarchyIncomingCall[] { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); - const declaration = firstOrOnly(CallHierarchy.resolveCallHierarchyDeclaration(program, position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position))); - return declaration ? CallHierarchy.getIncomingCalls(program, declaration, cancellationToken) : []; + const declaration = firstOrOnly(resolveCallHierarchyDeclaration(program, position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position))); + return declaration ? getIncomingCalls(program, declaration, cancellationToken) : []; } function provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[] { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); - const declaration = firstOrOnly(CallHierarchy.resolveCallHierarchyDeclaration(program, position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position))); - return declaration ? CallHierarchy.getOutgoingCalls(program, declaration) : []; + const declaration = firstOrOnly(resolveCallHierarchyDeclaration(program, position === 0 ? sourceFile : getTouchingPropertyName(sourceFile, position))); + return declaration ? getOutgoingCalls(program, declaration) : []; } function provideInlayHints(fileName: string, span: TextSpan, preferences: UserPreferences = emptyOptions): InlayHint[] { diff --git a/src/services/shims.ts b/src/services/shims.ts index 1c3bdae55e9f0..f9fa54bfdc834 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -1,71 +1,89 @@ +import { EmitOutput } from "../compiler/builderStatePublic"; +import { + parseJsonSourceFileConfigFileContent, +} from "../compiler/commandLineParser"; import { - Classifications, - Classifier, clear, - CompilerOptions, - CompletionEntryData, - createClassifier, - createDocumentRegistry, createGetCanonicalFileName, - createLanguageService, - createTextChangeRange, - createTextSpan, - Diagnostic, - diagnosticCategoryName, - DocCommentTemplateOptions, - DocumentRegistry, - EditorOptions, - EmitOutput, - emptyOptions, - EndOfLineState, - Extension, - extensionFromPath, - FileReference, filter, - flattenDiagnosticMessageText, - FormatCodeOptions, - FormatCodeSettings, - getAutomaticTypeDirectiveNames, - GetCompletionsAtPositionOptions, - getDefaultCompilerOptions, - getDirectoryPath, - getFileMatcherPatterns, - getNewLineOrDefaultFromHost, getProperty, - getSnapshotText, - HostCancellationToken, - IScriptSnapshot, isString, - JsTyping, - LanguageService, - LanguageServiceHost, map, - MapLike, - ModuleResolutionHost, + toFileNameLowerCase, +} from "../compiler/core"; +import { MapLike } from "../compiler/corePublic"; +import { extensionFromPath } from "../compiler/extension"; +import { getFileMatcherPatterns } from "../compiler/fileMatcher"; +import { + getAutomaticTypeDirectiveNames, + resolveModuleName, + resolveTypeReferenceDirective, +} from "../compiler/moduleNameResolver"; +import { parseJsonText } from "../compiler/parser"; +import { + getDirectoryPath, normalizeSlashes, + toPath, +} from "../compiler/path"; +import { timestamp } from "../compiler/performanceCore"; +import { flattenDiagnosticMessageText } from "../compiler/program"; +import { + CompilerOptions, + Diagnostic, + diagnosticCategoryName, + Extension, + FileReference, + ModuleResolutionHost, OperationCanceledException, ParseConfigHost, - parseJsonSourceFileConfigFileContent, - parseJsonText, - preProcessFile, ResolvedModuleFull, ResolvedTypeReferenceDirective, - resolveModuleName, - resolveTypeReferenceDirective, ScriptKind, - SemanticClassificationFormat, - servicesVersion, - SignatureHelpItemsOptions, TextChangeRange, TextRange, TextSpan, - ThrottledCancellationToken, - timestamp, - toFileNameLowerCase, - toPath, TypeAcquisition, UserPreferences, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + createTextChangeRange, + createTextSpan, +} from "../compiler/utilitiesPublic"; +import * as JsTyping from "../jsTyping/jsTyping"; +import { createClassifier } from "./classifier"; +import { + createDocumentRegistry, + DocumentRegistry, +} from "./documentRegistry"; +import { preProcessFile } from "./preProcess"; +import { + createLanguageService, + getDefaultCompilerOptions, + servicesVersion, + ThrottledCancellationToken, +} from "./services"; +import { + Classifications, + Classifier, + CompletionEntryData, + DocCommentTemplateOptions, + EditorOptions, + emptyOptions, + EndOfLineState, + FormatCodeOptions, + FormatCodeSettings, + GetCompletionsAtPositionOptions, + HostCancellationToken, + IScriptSnapshot, + LanguageService, + LanguageServiceHost, + SemanticClassificationFormat, + SignatureHelpItemsOptions, +} from "./types"; +import { + getNewLineOrDefaultFromHost, + getSnapshotText, +} from "./utilities"; // // Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index e5a48acc64076..6acca1cc42ad1 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -1,93 +1,105 @@ import { - ArrowFunction, - BinaryExpression, - CallLikeExpression, - CancellationToken, - canHaveSymbol, - CheckFlags, contains, countWhere, - createPrinterWithRemoveComments, - createTextSpan, - createTextSpanFromBounds, - createTextSpanFromNode, - Debug, - EmitHint, emptyArray, - Expression, - factory, - findAncestor, - findContainingList, findIndex, - findPrecedingToken, - findTokenOnLeftOfPosition, first, firstDefined, flatMapToMutable, - FunctionExpression, - getInvokedExpression, - getPossibleGenericSignatures, - getPossibleTypeArgumentsInfo, - Identifier, identity, - InternalSymbolName, + last, + lastOrUndefined, + map, + tryCast, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { createPrinterWithRemoveComments } from "../compiler/emitter"; +import { factory } from "../compiler/factory/nodeFactory"; +import { isArrayBindingPattern, isBinaryExpression, isBindingElement, isBlock, - isCallOrNewExpression, isFunctionTypeNode, isIdentifier, - isInComment, - isInsideTemplateLiteral, - isInString, - isJsxOpeningLikeElement, isMethodDeclaration, isNoSubstitutionTemplateLiteral, isObjectBindingPattern, isParameter, isPropertyAccessExpression, isSourceFile, - isSourceFileJS, isTaggedTemplateExpression, isTemplateHead, - isTemplateLiteralToken, isTemplateSpan, isTemplateTail, - isTransientSymbol, - last, - lastOrUndefined, +} from "../compiler/factory/nodeTests"; +import { skipTrivia } from "../compiler/scanner"; +import { + ArrowFunction, + BinaryExpression, + CallLikeExpression, + CancellationToken, + CheckFlags, + EmitHint, + Expression, + FunctionExpression, + Identifier, + InternalSymbolName, ListFormat, - map, - mapToDisplayParts, Node, NodeBuilderFlags, ParameterDeclaration, ParenthesizedExpression, Printer, Program, - punctuationPart, - rangeContainsRange, Signature, - SignatureHelpItem, - SignatureHelpItems, - SignatureHelpParameter, - SignatureHelpTriggerReason, - skipTrivia, SourceFile, - spacePart, Symbol, - SymbolDisplayPart, - symbolToDisplayParts, SyntaxKind, TaggedTemplateExpression, TemplateExpression, TextSpan, - tryCast, Type, TypeChecker, TypeParameter, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + getInvokedExpression, + isSourceFileJS, + isTransientSymbol, +} from "../compiler/utilities"; +import { + canHaveSymbol, + createTextSpan, + createTextSpanFromBounds, + findAncestor, + isCallOrNewExpression, + isJsxOpeningLikeElement, + isTemplateLiteralToken, +} from "../compiler/utilitiesPublic"; +import { + SignatureHelpItem, + SignatureHelpItems, + SignatureHelpParameter, + SignatureHelpTriggerReason, + SymbolDisplayPart, +} from "./types"; +import { + createTextSpanFromNode, + findContainingList, + findPrecedingToken, + findTokenOnLeftOfPosition, + getPossibleGenericSignatures, + getPossibleTypeArgumentsInfo, + isInComment, + isInsideTemplateLiteral, + isInString, + mapToDisplayParts, + punctuationPart, + rangeContainsRange, + spacePart, + symbolToDisplayParts, +} from "./utilities"; const enum InvocationKind { Call, TypeArgs, Contextual } interface CallInvocation { readonly kind: InvocationKind.Call; readonly node: CallLikeExpression; } @@ -683,10 +695,12 @@ function getTypeHelpItem(symbol: Symbol, typeParameters: readonly TypeParameter[ const documentation = symbol.getDocumentationComment(checker); const tags = symbol.getJsDocTags(checker); const prefixDisplayParts = [...typeSymbolDisplay, punctuationPart(SyntaxKind.LessThanToken)]; - return { isVariadic: false, prefixDisplayParts, suffixDisplayParts: [punctuationPart(SyntaxKind.GreaterThanToken)], separatorDisplayParts, parameters, documentation, tags }; + return { isVariadic: false, prefixDisplayParts, suffixDisplayParts: [punctuationPart(SyntaxKind.GreaterThanToken)], separatorDisplayParts: getSeparatorDisplayParts(), parameters, documentation, tags }; } -const separatorDisplayParts: SymbolDisplayPart[] = [punctuationPart(SyntaxKind.CommaToken), spacePart()]; +function getSeparatorDisplayParts(): SymbolDisplayPart[] { + return [punctuationPart(SyntaxKind.CommaToken), spacePart()]; +} function getSignatureHelpItem(candidateSignature: Signature, callTargetDisplayParts: readonly SymbolDisplayPart[], isTypeParameterList: boolean, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItem[] { const infos = (isTypeParameterList ? itemInfoForTypeParameters : itemInfoForParameters)(candidateSignature, checker, enclosingDeclaration, sourceFile); @@ -695,7 +709,7 @@ function getSignatureHelpItem(candidateSignature: Signature, callTargetDisplayPa const suffixDisplayParts = [...suffix, ...returnTypeToDisplayParts(candidateSignature, enclosingDeclaration, checker)]; const documentation = candidateSignature.getDocumentationComment(checker); const tags = candidateSignature.getJsDocTags(); - return { isVariadic, prefixDisplayParts, suffixDisplayParts, separatorDisplayParts, parameters, documentation, tags }; + return { isVariadic, prefixDisplayParts, suffixDisplayParts, separatorDisplayParts: getSeparatorDisplayParts(), parameters, documentation, tags }; }); } diff --git a/src/services/smartSelection.ts b/src/services/smartSelection.ts index 887fe86c0cbb2..06322bbbee19d 100644 --- a/src/services/smartSelection.ts +++ b/src/services/smartSelection.ts @@ -1,19 +1,16 @@ import { - CharacterCodes, compact, contains, - createTextSpanFromBounds, - Debug, findIndex, first, - getTokenPosOfNode, - getTouchingPropertyName, - getTrailingCommentRanges, - hasJSDocNodes, + last, + or, + singleOrUndefined, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { isBindingElement, isBlock, - isFunctionBody, - isFunctionLikeDeclaration, isImportDeclaration, isImportEqualsDeclaration, isJSDocSignature, @@ -26,27 +23,40 @@ import { isStringLiteral, isSyntaxList, isTemplateHead, - isTemplateLiteral, - isTemplateMiddleOrTemplateTail, isTemplateSpan, isTemplateTail, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, - last, +} from "../compiler/factory/nodeTests"; +import { parseNodeFactory } from "../compiler/parser"; +import { getTrailingCommentRanges } from "../compiler/scanner"; +import { + CharacterCodes, Node, - or, - parseNodeFactory, - positionsAreOnSameLine, - SelectionRange, - setTextRangePosEnd, - singleOrUndefined, SourceFile, SyntaxKind, SyntaxList, +} from "../compiler/types"; +import { + getTokenPosOfNode, + positionsAreOnSameLine, + setTextRangePosEnd, +} from "../compiler/utilities"; +import { + createTextSpanFromBounds, + hasJSDocNodes, + isFunctionBody, + isFunctionLikeDeclaration, + isTemplateLiteral, + isTemplateMiddleOrTemplateTail, textSpanIntersectsWithPosition, +} from "../compiler/utilitiesPublic"; +import { SelectionRange } from "./types"; +import { + getTouchingPropertyName, textSpansEqual, -} from "./_namespaces/ts"; +} from "./utilities"; /** @internal */ export function getSmartSelectionRange(pos: number, sourceFile: SourceFile): SelectionRange { diff --git a/src/services/sourcemaps.ts b/src/services/sourcemaps.ts index 6b0594c1df01a..1c3c87b955581 100644 --- a/src/services/sourcemaps.ts +++ b/src/services/sourcemaps.ts @@ -1,32 +1,41 @@ import { - base64decode, + createGetCanonicalFileName, + isString, +} from "../compiler/core"; +import { getDeclarationEmitOutputFilePathWorker } from "../compiler/emitterUtilities"; +import { removeFileExtension } from "../compiler/extension"; +import { isDeclarationFileName } from "../compiler/parser"; +import { + getDirectoryPath, + getNormalizedAbsolutePath, + toPath, +} from "../compiler/path"; +import { computeLineAndCharacterOfPosition, + getLineStarts, +} from "../compiler/scanner"; +import { createDocumentPositionMapper, - createGetCanonicalFileName, + getLineInfo, + identitySourceMapConsumer, + LineInfo, + tryGetSourceMappingURL, + tryParseRawSourceMap, +} from "../compiler/sourcemap"; +import { sys } from "../compiler/sys"; +import { DocumentPosition, DocumentPositionMapper, DocumentPositionMapperHost, Extension, - getDeclarationEmitOutputFilePathWorker, - getDirectoryPath, - getDocumentPositionMapper as ts_getDocumentPositionMapper, - getLineInfo, - getLineStarts, - getNormalizedAbsolutePath, - identitySourceMapConsumer, - isDeclarationFileName, - isString, LineAndCharacter, - LineInfo, - outFile, Program, - removeFileExtension, SourceFileLike, - sys, - toPath as ts_toPath, - tryGetSourceMappingURL, - tryParseRawSourceMap, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + base64decode, + outFile, +} from "../compiler/utilities"; const base64UrlRegExp = /^data:(?:application\/json(?:;charset=[uU][tT][fF]-8);base64,([A-Za-z0-9+\/=]+)$)?/; @@ -58,12 +67,12 @@ export function getSourceMapper(host: SourceMapperHost): SourceMapper { const documentPositionMappers = new Map(); return { tryGetSourcePosition, tryGetGeneratedPosition, toLineColumnOffset, clearCache }; - function toPath(fileName: string) { - return ts_toPath(fileName, currentDirectory, getCanonicalFileName); + function toPathWorker(fileName: string) { + return toPath(fileName, currentDirectory, getCanonicalFileName); } - function getDocumentPositionMapper(generatedFileName: string, sourceFileName?: string) { - const path = toPath(generatedFileName); + function getDocumentPositionMapperWorker(generatedFileName: string, sourceFileName?: string) { + const path = toPathWorker(generatedFileName); const value = documentPositionMappers.get(path); if (value) return value; @@ -73,7 +82,7 @@ export function getSourceMapper(host: SourceMapperHost): SourceMapper { } else if (host.readFile) { const file = getSourceFileLike(generatedFileName); - mapper = file && ts_getDocumentPositionMapper( + mapper = file && getDocumentPositionMapper( { getSourceFileLike, getCanonicalFileName, log: s => host.log(s) }, generatedFileName, getLineInfo(file.text, getLineStarts(file)), @@ -90,7 +99,7 @@ export function getSourceMapper(host: SourceMapperHost): SourceMapper { const file = getSourceFile(info.fileName); if (!file) return undefined; - const newLoc = getDocumentPositionMapper(info.fileName).getSourcePosition(info); + const newLoc = getDocumentPositionMapperWorker(info.fileName).getSourcePosition(info); return !newLoc || newLoc === info ? undefined : tryGetSourcePosition(newLoc) || newLoc; } @@ -114,7 +123,7 @@ export function getSourceMapper(host: SourceMapperHost): SourceMapper { getDeclarationEmitOutputFilePathWorker(info.fileName, program.getCompilerOptions(), currentDirectory, program.getCommonSourceDirectory(), getCanonicalFileName); if (declarationPath === undefined) return undefined; - const newLoc = getDocumentPositionMapper(declarationPath, info.fileName).getGeneratedPosition(info); + const newLoc = getDocumentPositionMapperWorker(declarationPath, info.fileName).getGeneratedPosition(info); return newLoc === info ? undefined : newLoc; } @@ -122,14 +131,14 @@ export function getSourceMapper(host: SourceMapperHost): SourceMapper { const program = host.getProgram(); if (!program) return undefined; - const path = toPath(fileName); + const path = toPathWorker(fileName); // file returned here could be .d.ts when asked for .ts file if projectReferences and module resolution created this source file const file = program.getSourceFileByPath(path); return file && file.resolvedPath === path ? file : undefined; } function getOrCreateSourceFileLike(fileName: string): SourceFileLike | undefined { - const path = toPath(fileName); + const path = toPathWorker(fileName); const fileFromCache = sourceFileLike.get(path); if (fileFromCache !== undefined) return fileFromCache ? fileFromCache : undefined; diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts index 61c23266701fa..a1b3881d391bc 100644 --- a/src/services/stringCompletions.ts +++ b/src/services/stringCompletions.ts @@ -1,154 +1,146 @@ +import { readJson } from "../compiler/commandLineParserUtilities"; import { - addToSeen, - altDirectorySeparator, arrayFrom, - CallLikeExpression, - CancellationToken, - CaseClause, - changeExtension, - CharacterCodes, - combinePaths, - comparePaths, - comparePatternKeys, compareStringsCaseSensitive, compareValues, - Comparison, - CompilerOptions, - CompletionEntry, - CompletionEntryDetails, - CompletionInfo, contains, - containsPath, - ContextFlags, createSortedArray, - createTextSpan, - createTextSpanFromStringLiteralLikeContent, - Debug, deduplicate, - directorySeparator, - ElementAccessExpression, emptyArray, endsWith, - ensureTrailingDirectorySeparator, equateStringsCaseSensitive, - Extension, - fileExtensionIsOneOf, filter, find, - findAncestor, - findPackageJson, - findPackageJsons, firstDefined, firstOrUndefined, flatMap, flatten, - forEachAncestorDirectory, - getBaseFileName, - getContextualTypeFromParent, - getDirectoryPath, - getEffectiveTypeRoots, - getEmitModuleResolutionKind, - getLeadingCommentRanges, - getModeForUsageLocation, - getModuleSpecifierEndingPreference, getOwnKeys, - getPackageJsonTypesVersionsPaths, - getPathComponents, - getPathsBasePath, - getReplacementSpanForContextToken, - getResolvePackageJsonExports, - getSupportedExtensions, - getSupportedExtensionsWithJsonIfResolveJsonModule, - getTextOfJsxAttributeName, - getTokenAtPosition, - hasIndexSignature, hasProperty, - hasTrailingDirectorySeparator, - hostGetCanonicalFileName, - IndexedAccessTypeNode, - isApplicableVersionedTypesKey, isArray, + isPatternMatch, + isString, + length, + mapDefined, + removePrefix, + singleElementArray, + startsWith, + stringContains, + tryRemovePrefix, +} from "../compiler/core"; +import { + Comparison, + MapLike, +} from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { + changeExtension, + getSupportedExtensions, + getSupportedExtensionsWithJsonIfResolveJsonModule, + removeFileExtension, + supportedTSImplementationExtensions, + tryGetExtensionFromPath, +} from "../compiler/extension"; +import { isCallExpression, isIdentifier, - isIdentifierText, - isImportCall, - isInReferenceComment, - isInString, isJsxAttribute, - isJsxOpeningLikeElement, isLiteralTypeNode, isObjectLiteralExpression, - isPatternMatch, - isPrivateIdentifierClassElementDeclaration, - isRootedDiskPath, - isString, isStringLiteral, - isStringLiteralLike, +} from "../compiler/factory/nodeTests"; +import { + comparePatternKeys, + getEffectiveTypeRoots, + getPackageJsonTypesVersionsPaths, + isApplicableVersionedTypesKey, + unmangleScopedPackageName, +} from "../compiler/moduleNameResolver"; +import { tryGetJSExtensionForFile, tryGetRealFileNameForNonJsDeclarationFileName } from "../compiler/moduleSpecifiers"; +import { getModuleSpecifierEndingPreference } from "../compiler/moduleSpecifiersUtilities"; +import { + altDirectorySeparator, + combinePaths, + comparePaths, + containsPath, + directorySeparator, + ensureTrailingDirectorySeparator, + fileExtensionIsOneOf, + forEachAncestorDirectory, + getBaseFileName, + getDirectoryPath, + getPathComponents, + hasTrailingDirectorySeparator, + isRootedDiskPath, isUrl, + normalizePath, + normalizeSlashes, + removeTrailingDirectorySeparator, + resolvePath, +} from "../compiler/path"; +import { getModeForUsageLocation } from "../compiler/program"; +import { + getLeadingCommentRanges, + isIdentifierText, +} from "../compiler/scanner"; +import { + CallLikeExpression, + CancellationToken, + CaseClause, + CharacterCodes, + CompilerOptions, + ContextFlags, + ElementAccessExpression, + Extension, + IndexedAccessTypeNode, JsxAttribute, - LanguageServiceHost, - length, LiteralExpression, LiteralTypeNode, - mapDefined, - MapLike, ModuleKind, - moduleResolutionUsesNodeModules, - ModuleSpecifierEnding, - moduleSpecifiers, - newCaseClauseTracker, Node, - normalizePath, - normalizeSlashes, ObjectLiteralExpression, Path, Program, PropertyAssignment, - rangeContainsPosition, - readJson, - removeFileExtension, - removePrefix, - removeTrailingDirectorySeparator, ResolutionMode, - resolvePath, - ScriptElementKind, - ScriptElementKindModifier, ScriptTarget, Signature, - signatureHasRestParameter, - SignatureHelp, - singleElementArray, - skipConstraint, - skipParentheses, SourceFile, - startsWith, - stringContains, StringLiteralLike, StringLiteralType, - stripQuotes, - supportedTSImplementationExtensions, Symbol, SyntaxKind, - textPart, TextSpan, - tryAndIgnoreErrors, - tryDirectoryExists, - tryFileExists, - tryGetDirectories, - tryGetExtensionFromPath, - tryParsePattern, - tryReadDirectory, - tryRemoveDirectoryPrefix, - tryRemovePrefix, Type, TypeChecker, TypeFlags, UnionTypeNode, - unmangleScopedPackageName, UserPreferences, +} from "../compiler/types"; +import { + addToSeen, + getEmitModuleResolutionKind, + getPathsBasePath, + getResolvePackageJsonExports, + getTextOfJsxAttributeName, + hostGetCanonicalFileName, + isImportCall, + ModuleSpecifierEnding, + signatureHasRestParameter, + skipParentheses, + stripQuotes, + tryParsePattern, + tryRemoveDirectoryPrefix, walkUpParenthesizedExpressions, walkUpParenthesizedTypes, -} from "./_namespaces/ts"; +} from "../compiler/utilities"; +import { + createTextSpan, + findAncestor, + isJsxOpeningLikeElement, + isPrivateIdentifierClassElementDeclaration, + isStringLiteralLike, +} from "../compiler/utilitiesPublic"; import { CompletionKind, createCompletionDetails, @@ -157,7 +149,40 @@ import { getPropertiesForObjectExpression, Log, SortText, -} from "./_namespaces/ts.Completions"; +} from "./completions"; +import { + ArgumentInfoForCompletions, + getArgumentInfoForCompletions, +} from "./signatureHelp"; +import { + CompletionEntry, + CompletionEntryDetails, + CompletionInfo, + LanguageServiceHost, + ScriptElementKind, + ScriptElementKindModifier, +} from "./types"; +import { + createTextSpanFromStringLiteralLikeContent, + findPackageJson, + findPackageJsons, + getContextualTypeFromParent, + getReplacementSpanForContextToken, + getTokenAtPosition, + hasIndexSignature, + isInReferenceComment, + isInString, + moduleResolutionUsesNodeModules, + newCaseClauseTracker, + rangeContainsPosition, + skipConstraint, + textPart, + tryAndIgnoreErrors, + tryDirectoryExists, + tryFileExists, + tryGetDirectories, + tryReadDirectory, +} from "./utilities"; interface NameAndKindSet { add(value: NameAndKind): void; @@ -383,7 +408,7 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL case SyntaxKind.NewExpression: case SyntaxKind.JsxAttribute: if (!isRequireCallArgument(node) && !isImportCall(parent)) { - const argumentInfo = SignatureHelp.getArgumentInfoForCompletions(parent.kind === SyntaxKind.JsxAttribute ? parent.parent : node, position, sourceFile); + const argumentInfo = getArgumentInfoForCompletions(parent.kind === SyntaxKind.JsxAttribute ? parent.parent : node, position, sourceFile); // Get string literal completions from specialized signatures of the target // i.e. declare function f(a: 'A'); // f("/*completion position*/") @@ -478,7 +503,7 @@ function getAlreadyUsedTypesInStringLiteralUnion(union: UnionTypeNode, current: type !== current && isLiteralTypeNode(type) && isStringLiteral(type.literal) ? type.literal.text : undefined); } -function getStringLiteralCompletionsFromSignature(call: CallLikeExpression, arg: StringLiteralLike, argumentInfo: SignatureHelp.ArgumentInfoForCompletions, checker: TypeChecker): StringLiteralCompletionsFromTypes | undefined { +function getStringLiteralCompletionsFromSignature(call: CallLikeExpression, arg: StringLiteralLike, argumentInfo: ArgumentInfoForCompletions, checker: TypeChecker): StringLiteralCompletionsFromTypes | undefined { let isNewIdentifier = false; const uniques = new Map(); const candidates: Signature[] = []; @@ -737,7 +762,7 @@ function getCompletionEntriesForDirectoryFragment( } function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerOptions, extensionOptions: ExtensionOptions): { name: string, extension: Extension | undefined } { - const nonJsResult = moduleSpecifiers.tryGetRealFileNameForNonJsDeclarationFileName(name); + const nonJsResult = tryGetRealFileNameForNonJsDeclarationFileName(name); if (nonJsResult) { return { name: nonJsResult, extension: tryGetExtensionFromPath(nonJsResult) }; } @@ -750,7 +775,7 @@ function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerO if (fileExtensionIsOneOf(name, supportedTSImplementationExtensions)) { return { name, extension: tryGetExtensionFromPath(name) }; } - const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(name, compilerOptions); + const outputExtension = tryGetJSExtensionForFile(name, compilerOptions); return outputExtension ? { name: changeExtension(name, outputExtension), extension: outputExtension } : { name, extension: tryGetExtensionFromPath(name) }; @@ -762,7 +787,7 @@ function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerO return { name: removeFileExtension(name), extension: tryGetExtensionFromPath(name) }; } - const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(name, compilerOptions); + const outputExtension = tryGetJSExtensionForFile(name, compilerOptions); return outputExtension ? { name: changeExtension(name, outputExtension), extension: outputExtension } : { name, extension: tryGetExtensionFromPath(name) }; diff --git a/src/services/suggestionDiagnostics.ts b/src/services/suggestionDiagnostics.ts index 3aa4a2d10966d..f80ecdca8e20c 100644 --- a/src/services/suggestionDiagnostics.ts +++ b/src/services/suggestionDiagnostics.ts @@ -1,65 +1,76 @@ import { addRange, + some, +} from "../compiler/core"; +import { Diagnostics } from "../compiler/diagnosticInformationMap.generated"; +import { + isBinaryExpression, + isBlock, + isCallExpression, + isExportAssignment, + isFunctionDeclaration, + isFunctionExpression, + isIdentifier, + isPropertyAccessExpression, + isReturnStatement, + isStringLiteral, + isVariableDeclaration, + isVariableStatement, +} from "../compiler/factory/nodeTests"; +import { forEachReturnStatement } from "../compiler/parserUtilities"; +import { fileExtensionIsOneOf } from "../compiler/path"; +import { getModeForUsageLocation } from "../compiler/program"; +import { AnyValidImportOrReExport, ArrowFunction, AssignmentDeclarationKind, Block, CallExpression, CancellationToken, - codefix, - compilerOptionsIndicateEsModules, - createDiagnosticForNode, - Diagnostics, DiagnosticWithLocation, Expression, ExpressionStatement, Extension, - fileExtensionIsOneOf, - forEachReturnStatement, FunctionDeclaration, FunctionExpression, - FunctionFlags, FunctionLikeDeclaration, - getAllowSyntheticDefaultImports, - getAssignmentDeclarationKind, - getFunctionFlags, - getModeForUsageLocation, - getResolvedModule, - hasInitializer, - hasPropertyAccessExpressionWithName, Identifier, - importFromModuleSpecifier, - isAsyncFunction, - isBinaryExpression, - isBlock, - isCallExpression, - isExportAssignment, - isFunctionDeclaration, - isFunctionExpression, - isFunctionLike, - isIdentifier, - isPropertyAccessExpression, - isRequireCall, - isReturnStatement, - isSourceFileJS, - isStringLiteral, - isVariableDeclaration, - isVariableStatement, MethodDeclaration, ModuleKind, Node, NodeFlags, Program, - programContainsEsModules, PropertyAccessExpression, ReturnStatement, - skipAlias, - some, SourceFile, SyntaxKind, TypeChecker, VariableStatement, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + createDiagnosticForNode, + FunctionFlags, + getAllowSyntheticDefaultImports, + getAssignmentDeclarationKind, + getFunctionFlags, + getResolvedModule, + importFromModuleSpecifier, + isAsyncFunction, + isRequireCall, + isSourceFileJS, + skipAlias, +} from "../compiler/utilities"; +import { + hasInitializer, + isFunctionLike, +} from "../compiler/utilitiesPublic"; +import { + compilerOptionsIndicateEsModules, + getJSDocTypedefNode, + hasPropertyAccessExpressionWithName, + parameterShouldGetTypeFromJSDoc, + programContainsEsModules, +} from "./utilities"; const visitedNestedConvertibleFunctions = new Map(); @@ -116,12 +127,12 @@ export function computeSuggestionDiagnostics(sourceFile: SourceFile, program: Pr } } - const jsdocTypedefNode = codefix.getJSDocTypedefNode(node); + const jsdocTypedefNode = getJSDocTypedefNode(node); if (jsdocTypedefNode) { diags.push(createDiagnosticForNode(jsdocTypedefNode, Diagnostics.JSDoc_typedef_may_be_converted_to_TypeScript_type)); } - if (codefix.parameterShouldGetTypeFromJSDoc(node)) { + if (parameterShouldGetTypeFromJSDoc(node)) { diags.push(createDiagnosticForNode(node.name || node, Diagnostics.JSDoc_types_may_be_moved_to_TypeScript_types)); } } diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index be2e83a48bb2f..508f93eac2ed1 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -1,112 +1,124 @@ import { addRange, arrayFrom, - BinaryExpression, - CallExpression, - CheckFlags, contains, - createPrinterWithRemoveComments, - Debug, - displayPart, - EmitHint, emptyArray, - EnumMember, - ExportAssignment, find, first, firstDefined, forEach, - GetAccessorDeclaration, - getCombinedLocalAndExportSymbolFlags, - getDeclarationOfKind, - getExternalModuleImportEqualsDeclarationExpression, - getMeaningFromLocation, - getNameOfDeclaration, - getNodeModifiers, - getObjectFlags, - getParseTreeNode, - getSourceFileOfNode, - getTextOfConstantValue, - getTextOfIdentifierOrLiteral, - getTextOfNode, - hasSyntacticModifier, - idText, - ImportEqualsDeclaration, + length, + some, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { createPrinterWithRemoveComments } from "../compiler/emitter"; +import { isArrowFunction, isBindingElement, isCallExpression, - isCallExpressionTarget, - isCallOrNewExpression, isClassExpression, - isConstTypeReference, - isDeprecatedDeclaration, - isEnumConst, isEnumDeclaration, - isExpression, - isExternalModuleImportEqualsDeclaration, - isFirstDeclarationOfSymbolParameter, - isFunctionBlock, isFunctionExpression, - isFunctionLike, isIdentifier, - isInExpressionContext, - isJsxOpeningLikeElement, - isLet, - isModuleWithStringLiteralName, - isNameOfFunctionDeclaration, - isNewExpressionTarget, isObjectBindingPattern, isTaggedTemplateExpression, - isThisInTypeQuery, - isTransientSymbol, isTypeAliasDeclaration, - isVarConst, - JSDocTagInfo, +} from "../compiler/factory/nodeTests"; +import { + BinaryExpression, + CallExpression, + CheckFlags, + EmitHint, + EnumMember, + ExportAssignment, + GetAccessorDeclaration, + ImportEqualsDeclaration, JsxOpeningLikeElement, - keywordPart, - length, - lineBreakPart, ListFormat, - mapToDisplayParts, ModifierFlags, ModuleDeclaration, NewExpression, Node, NodeBuilderFlags, ObjectFlags, - operatorPart, PropertyAccessExpression, PropertyDeclaration, - punctuationPart, - ScriptElementKind, - ScriptElementKindModifier, - SemanticMeaning, SetAccessorDeclaration, Signature, SignatureDeclaration, SignatureFlags, - signatureToDisplayParts, - some, SourceFile, - spacePart, Symbol, - SymbolDisplayPart, - SymbolDisplayPartKind, SymbolFlags, SymbolFormatFlags, - symbolToDisplayParts, SyntaxKind, TaggedTemplateExpression, - textOrKeywordPart, - textPart, TransientSymbol, Type, TypeChecker, TypeFormatFlags, TypeParameter, - typeToDisplayParts, VariableDeclaration, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + getCombinedLocalAndExportSymbolFlags, + getDeclarationOfKind, + getExternalModuleImportEqualsDeclarationExpression, + getObjectFlags, + getSourceFileOfNode, + getTextOfConstantValue, + getTextOfIdentifierOrLiteral, + getTextOfNode, + hasSyntacticModifier, + isEnumConst, + isExternalModuleImportEqualsDeclaration, + isFunctionBlock, + isInExpressionContext, + isLet, + isModuleWithStringLiteralName, + isThisInTypeQuery, + isTransientSymbol, + isVarConst, +} from "../compiler/utilities"; +import { + getNameOfDeclaration, + getParseTreeNode, + idText, + isCallOrNewExpression, + isConstTypeReference, + isExpression, + isFunctionLike, + isJsxOpeningLikeElement, +} from "../compiler/utilitiesPublic"; +import { + JSDocTagInfo, + ScriptElementKind, + ScriptElementKindModifier, + SymbolDisplayPart, + SymbolDisplayPartKind, +} from "./types"; +import { + displayPart, + getMeaningFromLocation, + getNodeModifiers, + isCallExpressionTarget, + isDeprecatedDeclaration, + isFirstDeclarationOfSymbolParameter, + isNameOfFunctionDeclaration, + isNewExpressionTarget, + keywordPart, + lineBreakPart, + mapToDisplayParts, + operatorPart, + punctuationPart, + SemanticMeaning, + signatureToDisplayParts, + spacePart, + symbolToDisplayParts, + textOrKeywordPart, + textPart, + typeToDisplayParts, +} from "./utilities"; const symbolDisplayNodeBuilderFlags = NodeBuilderFlags.OmitParameterModifiers | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope; diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index aac18cce972ed..299044633597d 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -1,175 +1,197 @@ import { - addToSeen, - ArrowFunction, - BindingElement, - CharacterCodes, - ClassElement, - ClassExpression, - ClassLikeDeclaration, - CommentRange, concatenate, - ConstructorDeclaration, contains, createMultiMap, - createNodeFactory, - createPrinter, - createRange, - createSourceFile, - createTextChange, - createTextRangeFromSpan, - createTextSpan, - createTextSpanFromRange, - createTextWriter, - Debug, - DeclarationStatement, - EmitHint, - EmitTextWriter, endsWith, - Expression, - factory, - FileTextChanges, filter, find, - findChildOfKind, findLastIndex, - findNextToken, - findPrecedingToken, first, firstOrUndefined, flatMap, flatMapToMutable, - formatting, - FunctionDeclaration, - FunctionExpression, - getAncestor, - getFirstNonSpaceCharacterPosition, - getFormatCodeSettingsForWriting, - getJSDocCommentRanges, - getLeadingCommentRanges, - getLineAndCharacterOfPosition, - getLineOfLocalPosition, - getLineStartPositionForPosition, - getNewLineKind, - getNewLineOrDefaultFromHost, - getNodeId, - getPrecedingNonSpaceCharacterPosition, - getScriptKindFromFileName, - getShebang, - getStartPositionOfLine, - getTokenAtPosition, - getTouchingToken, - getTrailingCommentRanges, group, - HasJSDoc, - hasJSDocNodes, - ImportClause, - ImportSpecifier, - indexOfNode, - InterfaceDeclaration, intersperse, - isAnyImportSyntax, isArray, + isLineBreak, + isString, + isWhiteSpaceLike, + isWhiteSpaceSingleLine, + last, + lastOrUndefined, + length, + mapDefined, + MultiMap, + removeSuffix, + singleOrUndefined, + stableSort, +} from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { createPrinter } from "../compiler/emitter"; +import { + createNodeFactory, + factory, + NodeFactoryFlags, +} from "../compiler/factory/nodeFactory"; +import { isArrowFunction, isCallExpression, - isClassElement, - isClassOrTypeElement, isExpressionStatement, isFunctionDeclaration, isFunctionExpression, - isFunctionLike, isIdentifier, isImportClause, isImportDeclaration, isImportSpecifier, - isInComment, - isInJSXText, - isInString, - isInTemplateString, isInterfaceDeclaration, - isJsonSourceFile, - isLineBreak, isNamedImports, isObjectLiteralExpression, isParameter, - isPinnedComment, - isPrologueDirective, isPropertyDeclaration, isPropertySignature, - isRecognizedTripleSlashComment, - isStatement, - isStatementButNotDeclaration, - isString, isStringLiteral, - isSuperCall, isVariableDeclaration, - isWhiteSpaceLike, - isWhiteSpaceSingleLine, +} from "../compiler/factory/nodeTests"; +import { createSourceFile } from "../compiler/parser"; +import { + getLeadingCommentRanges, + getLineAndCharacterOfPosition, + getShebang, + getTrailingCommentRanges, + skipTrivia, + tokenToString, +} from "../compiler/scanner"; +import { nullTransformationContext } from "../compiler/transformer"; +import { + ArrowFunction, + BindingElement, + CharacterCodes, + ClassElement, + ClassExpression, + ClassLikeDeclaration, + CommentRange, + ConstructorDeclaration, + DeclarationStatement, + EmitHint, + EmitTextWriter, + Expression, + FunctionDeclaration, + FunctionExpression, + HasJSDoc, + ImportClause, + ImportSpecifier, + InterfaceDeclaration, JSDoc, JSDocComment, JSDocParameterTag, JSDocReturnTag, JSDocTag, JSDocTypeTag, - LanguageServiceHost, - last, - lastOrUndefined, - length, - mapDefined, MethodSignature, Modifier, - MultiMap, NamedImportBindings, NamedImports, NamespaceImport, Node, NodeArray, - NodeFactoryFlags, - nodeIsSynthesized, - nullTransformationContext, ObjectLiteralElementLike, ObjectLiteralExpression, ParameterDeclaration, - positionsAreOnSameLine, PrintHandlers, PrologueDirective, PropertyAssignment, PropertyDeclaration, PropertySignature, - rangeContainsPosition, - rangeContainsRangeExclusive, - rangeOfNode, - rangeOfTypeParameters, - rangeStartPositionsAreOnSameLine, - removeSuffix, ScriptKind, ScriptTarget, - setTextRangePosEnd, SignatureDeclaration, - singleOrUndefined, - skipTrivia, SourceFile, SourceFileLike, - stableSort, Statement, - stringContainsAt, Symbol, SyntaxKind, - TextChange, TextRange, - textSpanEnd, Token, - tokenToString, TransformationContext, TypeLiteralNode, TypeNode, TypeParameterDeclaration, - UserPreferences, VariableDeclaration, VariableStatement, + Visitor, +} from "../compiler/types"; +import { + addToSeen, + createRange, + createTextWriter, + getAncestor, + getJSDocCommentRanges, + getLineOfLocalPosition, + getNodeId, + getScriptKindFromFileName, + getStartPositionOfLine, + indexOfNode, + isAnyImportSyntax, + isJsonSourceFile, + isPinnedComment, + isPrologueDirective, + isRecognizedTripleSlashComment, + isSuperCall, + nodeIsSynthesized, + positionsAreOnSameLine, + rangeOfNode, + rangeOfTypeParameters, + rangeStartPositionsAreOnSameLine, + setTextRangePosEnd, +} from "../compiler/utilities"; +import { + createTextSpan, + hasJSDocNodes, + isClassElement, + isClassOrTypeElement, + isFunctionLike, + isStatement, + isStatementButNotDeclaration, + textSpanEnd, +} from "../compiler/utilitiesPublic"; +import { visitEachChild, visitNodes, - Visitor, -} from "./_namespaces/ts"; +} from "../compiler/visitorPublic"; +import { + formatDocument, + formatNodeGivenIndentation, +} from "./formatting/formatting"; +import { SmartIndenter } from "./formatting/smartIndenter"; +import { + FileTextChanges, + FormatContext, + TextChange, + TextChangesContext, +} from "./types"; +import { + createTextChange, + createTextRangeFromSpan, + createTextSpanFromRange, + findChildOfKind, + findNextToken, + findPrecedingToken, + getFirstNonSpaceCharacterPosition, + getFormatCodeSettingsForWriting, + getLineStartPositionForPosition, + getNewLineKind, + getNewLineOrDefaultFromHost, + getPrecedingNonSpaceCharacterPosition, + getTokenAtPosition, + getTouchingToken, + isInComment, + isInJSXText, + isInString, + isInTemplateString, + rangeContainsPosition, + rangeContainsRangeExclusive, + stringContainsAt, +} from "./utilities"; /** * Currently for simplicity we store recovered positions on the node itself. @@ -466,13 +488,6 @@ function isSeparator(node: Node, candidate: Node | undefined): candidate is Toke return !!candidate && !!node.parent && (candidate.kind === SyntaxKind.CommaToken || (candidate.kind === SyntaxKind.SemicolonToken && node.parent.kind === SyntaxKind.ObjectLiteralExpression)); } -/** @internal */ -export interface TextChangesContext { - host: LanguageServiceHost; - formatContext: formatting.FormatContext; - preferences: UserPreferences; -} - /** @internal */ export type TypeAnnotatable = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature; @@ -502,7 +517,7 @@ export class ChangeTracker { } /** Public for tests only. Other callers should use `ChangeTracker.with`. */ - constructor(private readonly newLineCharacter: string, private readonly formatContext: formatting.FormatContext) {} + constructor(private readonly newLineCharacter: string, private readonly formatContext: FormatContext) {} public pushRaw(sourceFile: SourceFile, change: FileTextChanges) { Debug.assertEqual(sourceFile.fileName, change.fileName); @@ -840,7 +855,7 @@ export class ChangeTracker { return undefined; } const memberStart = member.getStart(sourceFile); - const memberIndentation = formatting.SmartIndenter.findFirstNonWhitespaceColumn(getLineStartPositionForPosition(memberStart, sourceFile), memberStart, sourceFile, this.formatContext.options); + const memberIndentation = SmartIndenter.findFirstNonWhitespaceColumn(getLineStartPositionForPosition(memberStart, sourceFile), memberStart, sourceFile, this.formatContext.options); if (indentation === undefined) { indentation = memberIndentation; } @@ -855,7 +870,7 @@ export class ChangeTracker { private computeIndentationForNewMember(sourceFile: SourceFile, node: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression | TypeLiteralNode) { const nodeStart = node.getStart(sourceFile); - return formatting.SmartIndenter.findFirstNonWhitespaceColumn(getLineStartPositionForPosition(nodeStart, sourceFile), nodeStart, sourceFile, this.formatContext.options) + return SmartIndenter.findFirstNonWhitespaceColumn(getLineStartPositionForPosition(nodeStart, sourceFile), nodeStart, sourceFile, this.formatContext.options) + (this.formatContext.options.indentSize ?? 4); } @@ -999,7 +1014,7 @@ export class ChangeTracker { * i.e. arguments in arguments lists, parameters in parameter lists etc. * Note that separators are part of the node in statements and class elements. */ - public insertNodeInListAfter(sourceFile: SourceFile, after: Node, newNode: Node, containingList = formatting.SmartIndenter.getContainingList(after, sourceFile)): void { + public insertNodeInListAfter(sourceFile: SourceFile, after: Node, newNode: Node, containingList = SmartIndenter.getContainingList(after, sourceFile)): void { if (!containingList) { Debug.fail("node is not a list element"); return; @@ -1071,7 +1086,7 @@ export class ChangeTracker { // insert separator immediately following the 'after' node to preserve comments in trailing trivia this.replaceRange(sourceFile, createRange(end), factory.createToken(separator)); // use the same indentation as 'after' item - const indentation = formatting.SmartIndenter.findFirstNonWhitespaceColumn(afterStartLinePosition, afterStart, sourceFile, this.formatContext.options); + const indentation = SmartIndenter.findFirstNonWhitespaceColumn(afterStartLinePosition, afterStart, sourceFile, this.formatContext.options); // insert element before the line break on the line that contains 'after' element let insertPos = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true, /*stopAtComments*/ false); // find position before "\n" or "\r\n" @@ -1122,7 +1137,7 @@ export class ChangeTracker { deletedNodesInLists.forEach(node => { const sourceFile = node.getSourceFile(); - const list = formatting.SmartIndenter.getContainingList(node, sourceFile)!; + const list = SmartIndenter.getContainingList(node, sourceFile)!; if (node !== last(list)) return; const lastNonDeletedIndex = findLastIndex(list, n => !deletedNodesInLists.has(n), list.length - 2); @@ -1224,7 +1239,7 @@ function getMembersOrProperties(node: ClassLikeDeclaration | InterfaceDeclaratio export type ValidateNonFormattedText = (node: Node, text: string) => void; namespace changesToText { - export function getTextChangesFromChanges(changes: readonly Change[], newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): FileTextChanges[] { + export function getTextChangesFromChanges(changes: readonly Change[], newLineCharacter: string, formatContext: FormatContext, validate: ValidateNonFormattedText | undefined): FileTextChanges[] { return mapDefined(group(changes, c => c.sourceFile.path), changesInFile => { const sourceFile = changesInFile[0].sourceFile; // order changes by start position @@ -1251,20 +1266,20 @@ namespace changesToText { }); } - export function newFileChanges(fileName: string, insertions: readonly NewFileInsertion[], newLineCharacter: string, formatContext: formatting.FormatContext): FileTextChanges { + export function newFileChanges(fileName: string, insertions: readonly NewFileInsertion[], newLineCharacter: string, formatContext: FormatContext): FileTextChanges { const text = newFileChangesWorker(getScriptKindFromFileName(fileName), insertions, newLineCharacter, formatContext); return { fileName, textChanges: [createTextChange(createTextSpan(0, 0), text)], isNewFile: true }; } - export function newFileChangesWorker(scriptKind: ScriptKind, insertions: readonly NewFileInsertion[], newLineCharacter: string, formatContext: formatting.FormatContext): string { + export function newFileChangesWorker(scriptKind: ScriptKind, insertions: readonly NewFileInsertion[], newLineCharacter: string, formatContext: FormatContext): string { // TODO: this emits the file, parses it back, then formats it that -- may be a less roundabout way to do this const nonFormattedText = flatMap(insertions, insertion => insertion.statements.map(s => s === SyntaxKind.NewLineTrivia ? "" : getNonformattedText(s, insertion.oldFile, newLineCharacter).text)).join(newLineCharacter); const sourceFile = createSourceFile("any file name", nonFormattedText, ScriptTarget.ESNext, /*setParentNodes*/ true, scriptKind); - const changes = formatting.formatDocument(sourceFile, formatContext); + const changes = formatDocument(sourceFile, formatContext); return applyChanges(nonFormattedText, changes) + newLineCharacter; } - function computeNewText(change: Change, sourceFile: SourceFile, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string { + function computeNewText(change: Change, sourceFile: SourceFile, newLineCharacter: string, formatContext: FormatContext, validate: ValidateNonFormattedText | undefined): string { if (change.kind === ChangeKind.Remove) { return ""; } @@ -1285,16 +1300,16 @@ namespace changesToText { } /** Note: this may mutate `nodeIn`. */ - function getFormattedTextOfNode(nodeIn: Node, sourceFile: SourceFile, pos: number, { indentation, prefix, delta }: InsertNodeOptions, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string { + function getFormattedTextOfNode(nodeIn: Node, sourceFile: SourceFile, pos: number, { indentation, prefix, delta }: InsertNodeOptions, newLineCharacter: string, formatContext: FormatContext, validate: ValidateNonFormattedText | undefined): string { const { node, text } = getNonformattedText(nodeIn, sourceFile, newLineCharacter); if (validate) validate(node, text); const formatOptions = getFormatCodeSettingsForWriting(formatContext, sourceFile); const initialIndentation = indentation !== undefined ? indentation - : formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix === newLineCharacter || getLineStartPositionForPosition(pos, sourceFile) === pos); + : SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix === newLineCharacter || getLineStartPositionForPosition(pos, sourceFile) === pos); if (delta === undefined) { - delta = formatting.SmartIndenter.shouldIndentChildNode(formatOptions, nodeIn) ? (formatOptions.indentSize || 0) : 0; + delta = SmartIndenter.shouldIndentChildNode(formatOptions, nodeIn) ? (formatOptions.indentSize || 0) : 0; } const file: SourceFileLike = { @@ -1303,7 +1318,7 @@ namespace changesToText { return getLineAndCharacterOfPosition(this, pos); } }; - const changes = formatting.formatNodeGivenIndentation(node, file, sourceFile.languageVariant, initialIndentation, delta, { ...formatContext, options: formatOptions }); + const changes = formatNodeGivenIndentation(node, file, sourceFile.languageVariant, initialIndentation, delta, { ...formatContext, options: formatOptions }); return applyChanges(text, changes); } @@ -1338,9 +1353,7 @@ function isTrivia(s: string) { // are more aggressive than is strictly necessary. const textChangesTransformationContext: TransformationContext = { ...nullTransformationContext, - factory: createNodeFactory( - nullTransformationContext.factory.flags | NodeFactoryFlags.NoParenthesizerRules, - nullTransformationContext.factory.baseFactory), + factory: createNodeFactory(factory.flags | NodeFactoryFlags.NoParenthesizerRules, factory.baseFactory), }; /** @internal */ @@ -1807,7 +1820,7 @@ export function deleteNode(changes: ChangeTracker, sourceFile: SourceFile, node: } function deleteNodeInList(changes: ChangeTracker, deletedNodesInLists: Set, sourceFile: SourceFile, node: Node): void { - const containingList = Debug.checkDefined(formatting.SmartIndenter.getContainingList(node, sourceFile)); + const containingList = Debug.checkDefined(SmartIndenter.getContainingList(node, sourceFile)); const index = indexOfNode(containingList, node); Debug.assert(index !== -1); if (containingList.length === 1) { diff --git a/src/services/transform.ts b/src/services/transform.ts index 85fae890c0984..1900bfce9a7f7 100644 --- a/src/services/transform.ts +++ b/src/services/transform.ts @@ -1,15 +1,17 @@ import { - CompilerOptions, concatenate, - DiagnosticWithLocation, - factory, - fixupCompilerOptions, isArray, +} from "../compiler/core"; +import { factory } from "../compiler/factory/nodeFactory"; +import { transformNodes } from "../compiler/transformer"; +import { + CompilerOptions, + DiagnosticWithLocation, Node, TransformationResult, TransformerFactory, - transformNodes, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { fixupCompilerOptions } from "./transpile"; /** * Transform one or more nodes using the supplied transformers. diff --git a/src/services/transpile.ts b/src/services/transpile.ts index 0f7e2b238c002..bcb8ae6b0b99a 100644 --- a/src/services/transpile.ts +++ b/src/services/transpile.ts @@ -1,32 +1,42 @@ +import { + createCompilerDiagnosticForInvalidCustomType, + optionDeclarations, + parseCustomTypeOption, + transpileOptionValueCompilerOptions, +} from "../compiler/commandLineParser"; import { addRange, - cloneCompilerOptions, + filter, + hasProperty, + isString, +} from "../compiler/core"; +import { MapLike } from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { createSourceFile } from "../compiler/parser"; +import { + fileExtensionIs, + normalizePath, + toPath, +} from "../compiler/path"; +import { + createProgram, + getImpliedNodeFormatForFile, + getSetExternalModuleIndicator, +} from "../compiler/program"; +import { CommandLineOptionOfCustomType, CompilerHost, CompilerOptions, - createCompilerDiagnosticForInvalidCustomType, - createProgram, - createSourceFile, CustomTransformers, - Debug, Diagnostic, - fileExtensionIs, - filter, +} from "../compiler/types"; +import { forEachEntry, - getDefaultCompilerOptions, getEmitScriptTarget, - getImpliedNodeFormatForFile, getNewLineCharacter, - getSetExternalModuleIndicator, - hasProperty, - isString, - MapLike, - normalizePath, - optionDeclarations, - parseCustomTypeOption, - toPath, - transpileOptionValueCompilerOptions, -} from "./_namespaces/ts"; +} from "../compiler/utilities"; +import { getDefaultCompilerOptions } from "./services"; +import { cloneCompilerOptions } from "./utilities"; export interface TranspileOptions { compilerOptions?: CompilerOptions; diff --git a/src/services/types.ts b/src/services/types.ts index beccf81d1f109..f158f1025f3f6 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -1,22 +1,23 @@ +import { EmitOutput } from "../compiler/builderStatePublic"; +import { ModuleResolutionCache } from "../compiler/moduleNameResolver"; +import { SymlinkCache } from "../compiler/symlinkCache"; import { + __String, CancellationToken, CompilerHost, CompilerOptions, CustomTransformers, Diagnostic, DiagnosticWithLocation, - DocumentHighlights, DocumentPositionMapper, - EmitOutput, - ExportInfoMap, FileReference, GetEffectiveTypeRootsHost, HasChangedAutomaticTypeDirectiveNames, HasInvalidatedResolutions, LineAndCharacter, MinimalResolutionCacheHost, - ModuleResolutionCache, ModuleSpecifierCache, + Node, ParsedCommandLine, Path, Program, @@ -30,16 +31,19 @@ import { ScriptKind, SourceFile, SourceFileLike, - SourceMapper, StringLiteralLike, Symbol, - SymlinkCache, + SymbolFlags, + SyntaxKind, TextChangeRange, - textChanges, TextRange, TextSpan, + TriviaSyntaxKind, + TypeChecker, UserPreferences, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { RulesMap } from "./formatting/rulesMap"; +import { SourceMapper } from "./sourcemaps"; declare module "../compiler/types" { // Module transform: converted from interface augmentation @@ -280,11 +284,6 @@ export interface ProjectPackageJsonInfo { has(dependencyName: string, inGroups?: PackageJsonDependencyGroup): boolean; } -/** @internal */ -export interface FormattingHost { - getNewLine?(): string; -} - /** @internal */ export const enum PackageJsonAutoImportPreference { Off, @@ -429,6 +428,39 @@ export interface LanguageServiceHost extends GetEffectiveTypeRootsHost, MinimalR /** @internal */ getIncompleteCompletionsCache?(): IncompleteCompletionsCache; } +/** @internal */ +export interface ExportInfoMap { + isUsableByFile(importingFile: Path): boolean; + clear(): void; + add(importingFile: Path, symbol: Symbol, key: __String, moduleSymbol: Symbol, moduleFile: SourceFile | undefined, exportKind: ExportKind, isFromPackageJson: boolean, checker: TypeChecker): void; + get(importingFile: Path, key: string): readonly SymbolExportInfo[] | undefined; + search(importingFile: Path, preferCapitalized: boolean, matches: (name: string, targetFlags: SymbolFlags) => boolean, action: (info: readonly SymbolExportInfo[], symbolName: string, isFromAmbientModule: boolean, key: string) => T | undefined): T | undefined; + releaseSymbols(): void; + isEmpty(): boolean; + /** @returns Whether the change resulted in the cache being cleared */ + onFileChanged(oldSourceFile: SourceFile, newSourceFile: SourceFile, typeAcquisitionEnabled: boolean): boolean; +} + +/** @internal */ +export const enum ExportKind { + Named, + Default, + ExportEquals, + UMD, +} + +/** @internal */ +export interface SymbolExportInfo { + readonly symbol: Symbol; + readonly moduleSymbol: Symbol; + /** Set if `moduleSymbol` is an external module, not an ambient module */ + moduleFileName: string | undefined; + exportKind: ExportKind; + targetFlags: SymbolFlags; + /** True if export was only found via the package.json AutoImportProvider (for telemetry). */ + isFromPackageJson: boolean; +} + /** @internal */ export const emptyOptions = {}; @@ -1061,6 +1093,11 @@ export interface HighlightSpan { kind: HighlightSpanKind; } +export interface DocumentHighlights { + fileName: string; + highlightSpans: HighlightSpan[]; +} + export interface NavigateToItem { name: string; kind: ScriptElementKind; @@ -1149,6 +1186,12 @@ export interface FormatCodeSettings extends EditorSettings { readonly indentSwitchCase?: boolean; } +/** @internal */ +export interface FormattingHost { + getNewLine?(): string; +} + + export function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings { return { indentSize: 4, @@ -1765,13 +1808,27 @@ export interface CodeFixRegistration { } /** @internal */ -export interface CodeFixContextBase extends textChanges.TextChangesContext { +export interface CodeFixContextBase extends TextChangesContext { sourceFile: SourceFile; program: Program; cancellationToken: CancellationToken; preferences: UserPreferences; } +/** @internal */ +export interface FormatContext { + readonly options: FormatCodeSettings; + readonly host: FormattingHost; + readonly getRules: RulesMap; +} + +/** @internal */ +export interface TextChangesContext { + host: LanguageServiceHost; + formatContext: FormatContext; + preferences: UserPreferences; +} + /** @internal */ export interface CodeFixAllContext extends CodeFixContextBase { fixId: {}; @@ -1797,7 +1854,7 @@ export interface Refactor { } /** @internal */ -export interface RefactorContext extends textChanges.TextChangesContext { +export interface RefactorContext extends TextChangesContext { file: SourceFile; startPosition: number; endPosition?: number; @@ -1816,3 +1873,62 @@ export interface InlayHintsContext { span: TextSpan; preferences: UserPreferences; } + +/** + * Returned by refactor functions when some error message needs to be surfaced to users. + * @internal + */ +export interface RefactorErrorInfo { + error: string; +} + +/** @internal */ +export interface ContextWithStartAndEndNode { + start: Node; + end: Node; +} + +/** @internal */ +export type ContextNode = Node | ContextWithStartAndEndNode; + +/** @internal */ +export type Entry = NodeEntry | SpanEntry; + +/** @internal */ +export interface NodeEntry { + readonly kind: NodeEntryKind; + readonly node: Node; + readonly context?: ContextNode; +} + +/** @internal */ +export interface SpanEntry { + readonly kind: EntryKind.Span; + readonly fileName: string; + readonly textSpan: TextSpan; +} + +/** @internal */ +export const enum EntryKind { Span, Node, StringLiteral, SearchedLocalFoundProperty, SearchedPropertyFoundLocal } + +/** @internal */ +export type NodeEntryKind = + | EntryKind.Node + | EntryKind.StringLiteral + | EntryKind.SearchedLocalFoundProperty + | EntryKind.SearchedPropertyFoundLocal; + +/** @internal */ +export interface TextRangeWithKind extends TextRange { + kind: T; +} + +/** @internal */ +export type TextRangeWithTriviaKind = TextRangeWithKind; + +/** @internal */ +export interface TokenInfo { + leadingTrivia: TextRangeWithTriviaKind[] | undefined; + token: TextRangeWithKind; + trailingTrivia: TextRangeWithTriviaKind[] | undefined; +} diff --git a/src/services/utilities.ts b/src/services/utilities.ts index d7c8149609b0c..59b269ccad363 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1,211 +1,104 @@ import { - __String, - addEmitFlags, - addSyntheticLeadingComment, - addSyntheticTrailingComment, - AnyImportOrRequireStatement, + getModuleInstanceState, + ModuleInstanceState, +} from "../compiler/binder"; +import { setConfigFileInOptions } from "../compiler/commandLineParser"; +import { assertType, - AssignmentDeclarationKind, - BinaryExpression, binarySearchKey, - BindingElement, - BreakOrContinueStatement, - CallExpression, - canHaveModifiers, - CaseClause, cast, - CatchClause, - CharacterCodes, - ClassDeclaration, - ClassExpression, clone, - codefix, - combinePaths, - CommentKind, - CommentRange, compareTextSpans, compareValues, - Comparison, - CompilerOptions, - ConditionalExpression, + concatenate, contains, - ContextFlags, - createPrinterWithRemoveCommentsOmitTrailingSemicolon, - createRange, - createScanner, - createTextSpan, - createTextSpanFromBounds, - Debug, - Declaration, - Decorator, - DefaultClause, - defaultMaximumTruncationLength, - DeleteExpression, - Diagnostic, - DiagnosticAndArguments, - DiagnosticArguments, - DiagnosticMessage, - DiagnosticWithLocation, - directoryProbablyExists, - DisplayPartsSymbolWriter, - DocumentPosition, - DocumentSpan, - DoStatement, - ElementAccessExpression, - EmitFlags, - EmitHint, - emitModuleKindIsNonNodeESM, emptyArray, - EndOfFileToken, endsWith, - ensureScriptKind, - EqualityOperator, - escapeString, - ExportAssignment, - ExportDeclaration, - Expression, - ExpressionStatement, - factory, - FileTextChanges, filter, find, - findAncestor, - findConfigFile, first, firstDefined, firstOrUndefined, - forEachAncestorDirectory, - forEachChild, - forEachLeadingCommentRange, - forEachTrailingCommentRange, - FormatCodeSettings, - formatStringFromArgs, - formatting, - FormattingHost, - ForOfStatement, - FunctionDeclaration, - FunctionExpression, - FunctionLikeDeclaration, - getAssignmentDeclarationKind, - getCombinedNodeFlagsAlwaysIncludeJSDoc, - getDirectoryPath, - getEmitModuleKind, - getEmitScriptTarget, - getExternalModuleImportEqualsDeclarationExpression, - getImpliedNodeFormatForFile, - getIndentString, - getJSDocEnumTag, - getLastChild, - getLineAndCharacterOfPosition, - getLineStarts, - getLocaleSpecificMessage, - getModuleInstanceState, - getNameOfDeclaration, - getNodeId, - getPackageNameFromTypesPackageName, - getPathComponents, - getRootDeclaration, - getSourceFileOfNode, - getSpanOfTokenAtPosition, - getSymbolId, - getTextOfIdentifierOrLiteral, - getTextOfNode, - getTypesPackageName, - hasJSFileExtension, - hasSyntacticModifier, - HeritageClause, - hostGetCanonicalFileName, - Identifier, - identifierIsThisKeyword, + forEach, identity, - idText, - IfStatement, - ImportClause, - ImportDeclaration, - ImportSpecifier, - ImportTypeNode, - indexOfNode, - IndexSignatureDeclaration, - InternalSymbolName, - isAmbientModule, - isAnyImportSyntax, isArray, + isWhiteSpaceLike, + isWhiteSpaceSingleLine, + last, + lastOrUndefined, + map, + maybeBind, + notImplemented, + or, + singleOrUndefined, + some, + SortKind, + stableSort, + startsWith, + stringContains, + tryCast, +} from "../compiler/core"; +import { Comparison } from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { createPrinterWithRemoveCommentsOmitTrailingSemicolon } from "../compiler/emitter"; +import { hasJSFileExtension } from "../compiler/extension"; +import { + addEmitFlags, + addSyntheticLeadingComment, + addSyntheticTrailingComment, +} from "../compiler/factory/emitNode"; +import { factory } from "../compiler/factory/nodeFactory"; +import { isArrayBindingPattern, isArrayTypeNode, isAsExpression, isAwaitExpression, isBinaryExpression, isBindingElement, - isBreakOrContinueStatement, isCallExpression, - isCallOrNewExpression, isClassDeclaration, isClassExpression, isClassStaticBlockDeclaration, isConditionalTypeNode, - IScriptSnapshot, - isDeclaration, - isDeclarationName, isDecorator, isDefaultClause, isDeleteExpression, isElementAccessExpression, - isEntityName, isEnumDeclaration, isEnumMember, isExportAssignment, isExportDeclaration, isExportSpecifier, - isExpression, - isExpressionNode, - isExternalModule, - isExternalModuleImportEqualsDeclaration, + isExpressionStatement, isExternalModuleReference, - isFileLevelUniqueName, isForInStatement, isForOfStatement, - isFunctionBlock, isFunctionDeclaration, isFunctionExpression, - isFunctionLike, isGetAccessorDeclaration, - isGlobalScopeAugmentation, isHeritageClause, isIdentifier, - isImportCall, isImportClause, isImportDeclaration, isImportEqualsDeclaration, - isImportOrExportSpecifier, isImportSpecifier, isInferTypeNode, - isInJSFile, isInterfaceDeclaration, - isInternalModuleImportEqualsDeclaration, isJSDoc, - isJSDocCommentContainingNode, isJSDocLink, isJSDocLinkCode, - isJSDocLinkLike, isJSDocMemberName, isJSDocNameReference, - isJSDocTag, isJSDocTemplateTag, - isJSDocTypeAlias, + isJSDocTypedefTag, isJsxElement, isJsxExpression, - isJsxOpeningLikeElement, isJsxText, - isKeyword, isLabeledStatement, - isLet, - isLiteralExpression, isLiteralTypeNode, isMappedTypeNode, - isModifier, isModuleBlock, isModuleDeclaration, - isNamedDeclaration, isNamedExports, isNamedImports, isNamespaceExport, @@ -214,130 +107,158 @@ import { isNumericLiteral, isObjectBindingPattern, isObjectLiteralExpression, - isOptionalChain, - isOptionalChainRoot, isParameter, - isPartOfTypeNode, isPrivateIdentifier, isPropertyAccessExpression, - isPropertyNameLiteral, isQualifiedName, - isRequireCall, - isRequireVariableStatement, - isRightSideOfQualifiedNameOrPropertyAccess, - isRootedDiskPath, isSetAccessorDeclaration, isSourceFile, - isSourceFileJS, - isStringDoubleQuoted, isStringLiteral, - isStringLiteralLike, - isStringOrNumericLiteralLike, - isStringTextContainingNode, isSyntaxList, isTaggedTemplateExpression, - isTemplateLiteralKind, - isToken, - isTransientSymbol, isTypeAliasDeclaration, - isTypeElement, - isTypeNode, isTypeOfExpression, isTypeOperatorNode, isTypeParameterDeclaration, isTypeReferenceNode, - isVarConst, isVariableDeclarationList, + isVariableStatement, isVoidExpression, - isWhiteSpaceLike, - isWhiteSpaceSingleLine, isYieldExpression, +} from "../compiler/factory/nodeTests"; +import { + canHaveModifiers, + setOriginalNode, + setTextRange, +} from "../compiler/factory/utilitiesPublic"; +import { + getPackageNameFromTypesPackageName, + getTypesPackageName, +} from "../compiler/moduleNameResolver"; +import { getNodeModulesPackageName } from "../compiler/moduleSpecifiers"; +import { forEachChild } from "../compiler/parser"; +import { getLastChild } from "../compiler/parserUtilities"; +import { + combinePaths, + forEachAncestorDirectory, + getDirectoryPath, + getPathComponents, + isRootedDiskPath, + normalizePath, + pathIsRelative, + toPath, +} from "../compiler/path"; +import { findConfigFile, getImpliedNodeFormatForFile } from "../compiler/program"; +import { + createScanner, + forEachLeadingCommentRange, + forEachTrailingCommentRange, + getLineAndCharacterOfPosition, + getLineStarts, + getTrailingCommentRanges, + Scanner, + stringToToken, + tokenToString, +} from "../compiler/scanner"; +import { nullTransformationContext } from "../compiler/transformer"; +import { + __String, + AnyImportOrRequireStatement, + AssignmentDeclarationKind, + BinaryExpression, + BindingElement, + BreakOrContinueStatement, + CallExpression, + CaseClause, + CatchClause, + CharacterCodes, + ClassDeclaration, + ClassExpression, + CommentKind, + CommentRange, + CompilerOptions, + ConditionalExpression, + ContextFlags, + Declaration, + Decorator, + DefaultClause, + DeleteExpression, + Diagnostic, + DiagnosticAndArguments, + DiagnosticArguments, + DiagnosticMessage, + DiagnosticWithLocation, + DocumentPosition, + DoStatement, + ElementAccessExpression, + EmitFlags, + EmitHint, + EndOfFileToken, + EqualityOperator, + ExportAssignment, + ExportDeclaration, + Expression, + ExpressionStatement, + ForInOrOfStatement, + ForOfStatement, + FunctionDeclaration, + FunctionExpression, + FunctionLikeDeclaration, + HeritageClause, + Identifier, + IfStatement, + ImportClause, + ImportDeclaration, + ImportSpecifier, + ImportTypeNode, + IndexSignatureDeclaration, + InternalSymbolName, IterationStatement, JSDocLink, JSDocLinkCode, - JSDocLinkDisplayPart, JSDocLinkPlain, JSDocTypedefTag, - JsTyping, JsxEmit, JsxOpeningLikeElement, LabeledStatement, - LanguageServiceHost, - last, - lastOrUndefined, LiteralExpression, - map, - maybeBind, Modifier, ModifierFlags, ModuleDeclaration, - ModuleInstanceState, ModuleKind, ModuleResolutionKind, ModuleSpecifierResolutionHost, - moduleSpecifiers, Mutable, + NamedDeclaration, NewExpression, NewLineKind, Node, NodeArray, NodeBuilderFlags, NodeFlags, - nodeIsMissing, - nodeIsPresent, - nodeIsSynthesized, - normalizePath, NoSubstitutionTemplateLiteral, - notImplemented, - nullTransformationContext, NumericLiteral, - or, - OrganizeImports, - PackageJsonDependencyGroup, - parseBigInt, - pathIsRelative, + ParameterDeclaration, PrefixUnaryExpression, Program, - ProjectPackageJsonInfo, PropertyAccessExpression, PropertyAssignment, + PropertyDeclaration, PropertyName, + PropertySignature, PseudoBigInt, - pseudoBigIntToString, QualifiedName, - RefactorContext, - Scanner, - ScriptElementKind, - ScriptElementKindModifier, ScriptKind, ScriptTarget, - SemicolonPreference, - setConfigFileInOptions, - setOriginalNode, - setTextRange, Signature, SignatureDeclaration, - singleOrUndefined, - skipAlias, - skipOuterExpressions, - skipParentheses, - some, - SortKind, SourceFile, SourceFileLike, - SourceMapper, SpreadElement, - stableSort, - startsWith, - stringContains, StringLiteral, StringLiteralLike, - stringToToken, - stripQuotes, Symbol, SymbolAccessibility, - SymbolDisplayPart, - SymbolDisplayPartKind, SymbolFlags, SymbolFormatFlags, SymbolTracker, @@ -347,17 +268,9 @@ import { TemplateExpression, TemplateLiteralToken, TemplateSpan, - TextChange, - textChanges, TextRange, TextSpan, - textSpanContainsPosition, - textSpanContainsTextSpan, - textSpanEnd, Token, - tokenToString, - toPath, - tryCast, Type, TypeChecker, TypeFlags, @@ -365,14 +278,152 @@ import { TypeNode, TypeOfExpression, TypeQueryNode, - unescapeLeadingUnderscores, UserPreferences, VariableDeclaration, - visitEachChild, VoidExpression, - walkUpParenthesizedExpressions, YieldExpression, -} from "./_namespaces/ts"; +} from "../compiler/types"; +import { + createRange, + defaultMaximumTruncationLength, + directoryProbablyExists, + emitModuleKindIsNonNodeESM, + ensureScriptKind, + escapeString, + formatStringFromArgs, + getAssignmentDeclarationKind, + getEmitModuleKind, + getEmitScriptTarget, + getExternalModuleImportEqualsDeclarationExpression, + getIndentString, + getLeadingCommentRangesOfNode, + getLocaleSpecificMessage, + getNodeId, + getRootDeclaration, + getSourceFileOfNode, + getSpanOfTokenAtPosition, + getSymbolId, + getTextOfIdentifierOrLiteral, + getTextOfNode, + hasSyntacticModifier, + hostGetCanonicalFileName, + identifierIsThisKeyword, + indexOfNode, + isAmbientModule, + isAnyImportSyntax, + isDeclarationName, + isExpressionNode, + isExternalModule, + isExternalModuleImportEqualsDeclaration, + isFileLevelUniqueName, + isFunctionBlock, + isGlobalScopeAugmentation, + isImportCall, + isInJSFile, + isInternalModuleImportEqualsDeclaration, + isJSDocTypeAlias, + isKeyword, + isLet, + isPartOfTypeNode, + isPropertyNameLiteral, + isRequireCall, + isRequireVariableStatement, + isRightSideOfQualifiedNameOrPropertyAccess, + isSourceFileJS, + isStringDoubleQuoted, + isStringOrNumericLiteralLike, + isTransientSymbol, + isVarConst, + nodeIsMissing, + nodeIsPresent, + nodeIsSynthesized, + parseBigInt, + pseudoBigIntToString, + skipAlias, + skipOuterExpressions, + skipParentheses, + stripQuotes, + walkUpParenthesizedExpressions, +} from "../compiler/utilities"; +import { + createTextSpan, + createTextSpanFromBounds, + findAncestor, + getCombinedNodeFlagsAlwaysIncludeJSDoc, + getJSDocEnumTag, + getJSDocReturnType, + getJSDocType, + getNameOfDeclaration, + hasJSDocNodes, + idText, + isBreakOrContinueStatement, + isCallOrNewExpression, + isDeclaration, + isEntityName, + isExpression, + isForInOrOfStatement, + isFunctionLike, + isFunctionLikeDeclaration, + isImportOrExportSpecifier, + isJSDocCommentContainingNode, + isJSDocLinkLike, + isJSDocTag, + isJsxOpeningLikeElement, + isLiteralExpression, + isModifier, + isNamedDeclaration, + isOptionalChain, + isOptionalChainRoot, + isStringLiteralLike, + isStringTextContainingNode, + isTemplateLiteralKind, + isToken, + isTypeElement, + isTypeNode, + textSpanContainsPosition, + textSpanContainsTextSpan, + textSpanEnd, + unescapeLeadingUnderscores, +} from "../compiler/utilitiesPublic"; +import { visitEachChild } from "../compiler/visitorPublic"; +import * as JsTyping from "../jsTyping/jsTyping"; +import { moduleSymbolToValidIdentifier } from "./codefixes/importAdder"; +import { + compareImportsOrRequireStatements, + detectImportDeclarationSorting, + getImportDeclarationInsertionIndex, + getOrganizeImportsComparer, +} from "./organizeImports"; +import { DisplayPartsSymbolWriter } from "./services"; +import { SourceMapper } from "./sourcemaps"; +import { + ChangeTracker, + LeadingTriviaOption, +} from "./textChanges"; +import { + ContextNode, + ContextWithStartAndEndNode, + DocumentSpan, + Entry, + EntryKind, + FileTextChanges, + FormatCodeSettings, + FormatContext, + FormattingHost, + IScriptSnapshot, + JSDocLinkDisplayPart, + LanguageServiceHost, + PackageJsonDependencyGroup, + ProjectPackageJsonInfo, + RefactorContext, + ScriptElementKind, + ScriptElementKindModifier, + SemicolonPreference, + SymbolDisplayPart, + SymbolDisplayPartKind, + TextChange, + TextRangeWithKind, +} from "./types"; // These utilities are common to multiple language service features. //#region @@ -2118,7 +2169,7 @@ export function getPossibleTypeArgumentsInfo(tokenIn: Node | undefined, sourceFi * @internal */ export function isInComment(sourceFile: SourceFile, position: number, tokenAtPosition?: Node): CommentRange | undefined { - return formatting.getRangeOfEnclosingComment(sourceFile, position, /*precedingToken*/ undefined, tokenAtPosition); + return getRangeOfEnclosingComment(sourceFile, position, /*precedingToken*/ undefined, tokenAtPosition); } /** @internal */ @@ -2575,24 +2626,24 @@ export function findModifier(node: Node, kind: Modifier["kind"]): Modifier | und } /** @internal */ -export function insertImports(changes: textChanges.ChangeTracker, sourceFile: SourceFile, imports: AnyImportOrRequireStatement | readonly AnyImportOrRequireStatement[], blankLineBetween: boolean, preferences: UserPreferences): void { +export function insertImports(changes: ChangeTracker, sourceFile: SourceFile, imports: AnyImportOrRequireStatement | readonly AnyImportOrRequireStatement[], blankLineBetween: boolean, preferences: UserPreferences): void { const decl = isArray(imports) ? imports[0] : imports; const importKindPredicate: (node: Node) => node is AnyImportOrRequireStatement = decl.kind === SyntaxKind.VariableStatement ? isRequireVariableStatement : isAnyImportSyntax; const existingImportStatements = filter(sourceFile.statements, importKindPredicate); - let sortKind = isArray(imports) ? OrganizeImports.detectImportDeclarationSorting(imports, preferences) : SortKind.Both; - const comparer = OrganizeImports.getOrganizeImportsComparer(preferences, sortKind === SortKind.CaseInsensitive); - const sortedNewImports = isArray(imports) ? stableSort(imports, (a, b) => OrganizeImports.compareImportsOrRequireStatements(a, b, comparer)) : [imports]; + let sortKind = isArray(imports) ? detectImportDeclarationSorting(imports, preferences) : SortKind.Both; + const comparer = getOrganizeImportsComparer(preferences, sortKind === SortKind.CaseInsensitive); + const sortedNewImports = isArray(imports) ? stableSort(imports, (a, b) => compareImportsOrRequireStatements(a, b, comparer)) : [imports]; if (!existingImportStatements.length) { changes.insertNodesAtTopOfFile(sourceFile, sortedNewImports, blankLineBetween); } - else if (existingImportStatements && (sortKind = OrganizeImports.detectImportDeclarationSorting(existingImportStatements, preferences))) { - const comparer = OrganizeImports.getOrganizeImportsComparer(preferences, sortKind === SortKind.CaseInsensitive); + else if (existingImportStatements && (sortKind = detectImportDeclarationSorting(existingImportStatements, preferences))) { + const comparer = getOrganizeImportsComparer(preferences, sortKind === SortKind.CaseInsensitive); for (const newImport of sortedNewImports) { - const insertionIndex = OrganizeImports.getImportDeclarationInsertionIndex(existingImportStatements, newImport, comparer); + const insertionIndex = getImportDeclarationInsertionIndex(existingImportStatements, newImport, comparer); if (insertionIndex === 0) { // If the first import is top-of-file, insert after the leading comment which is likely the header. const options = existingImportStatements[0] === sourceFile.statements[0] ? - { leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude } : {}; + { leadingTriviaOption: LeadingTriviaOption.Exclude } : {}; changes.insertNodeBefore(sourceFile, existingImportStatements[0], newImport, /*blankLineBetween*/ false, options); } else { @@ -3817,7 +3868,7 @@ export function createPackageJsonImportFilter(fromFile: SourceFile, preferences: if (!stringContains(importedFileName, "node_modules")) { return undefined; } - const specifier = moduleSpecifiers.getNodeModulesPackageName( + const specifier = getNodeModulesPackageName( host.getCompilationSettings(), fromFile, importedFileName, @@ -3959,8 +4010,8 @@ export function getNamesForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTa if (needsNameFromDeclaration(symbol)) { const fromDeclaration = getDefaultLikeExportNameFromDeclaration(symbol); if (fromDeclaration) return fromDeclaration; - const fileNameCase = codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ false); - const capitalized = codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ true); + const fileNameCase = moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ false); + const capitalized = moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ true); if (fileNameCase === capitalized) return fileNameCase; return [fileNameCase, capitalized]; } @@ -3972,7 +4023,7 @@ export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTar if (needsNameFromDeclaration(symbol)) { // Name of "export default foo;" is "foo". Name of "export default 0" is the filename converted to camelCase. return getDefaultLikeExportNameFromDeclaration(symbol) - || codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, !!preferCapitalized); + || moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, !!preferCapitalized); } return symbol.name; @@ -4088,7 +4139,7 @@ export function diagnosticToString(diag: DiagnosticOrDiagnosticAndArguments): st * * @internal */ -export function getFormatCodeSettingsForWriting({ options }: formatting.FormatContext, sourceFile: SourceFile): FormatCodeSettings { +export function getFormatCodeSettingsForWriting({ options }: FormatContext, sourceFile: SourceFile): FormatCodeSettings { const shouldAutoDetectSemicolonPreference = !options.semicolons || options.semicolons === SemicolonPreference.Ignore; const shouldRemoveSemicolons = options.semicolons === SemicolonPreference.Remove || shouldAutoDetectSemicolonPreference && !probablyUsesSemicolons(sourceFile); return { @@ -4107,6 +4158,158 @@ export function isSourceFileFromLibrary(program: Program, node: SourceFile) { return program.isSourceFileFromExternalLibrary(node) || program.isSourceFileDefaultLibrary(node); } +/** @internal */ +export type DeclarationWithType = | FunctionLikeDeclaration | VariableDeclaration | PropertySignature | PropertyDeclaration; + +/** @internal */ +export function parameterShouldGetTypeFromJSDoc(node: Node): node is DeclarationWithType { + return isDeclarationWithType(node) && hasUsableJSDoc(node); +} + +function isDeclarationWithType(node: Node): node is DeclarationWithType { + return isFunctionLikeDeclaration(node) + || node.kind === SyntaxKind.VariableDeclaration + || node.kind === SyntaxKind.PropertySignature + || node.kind === SyntaxKind.PropertyDeclaration; +} + +function hasUsableJSDoc(decl: DeclarationWithType | ParameterDeclaration): boolean { + return isFunctionLikeDeclaration(decl) ? decl.parameters.some(hasUsableJSDoc) || (!decl.type && !!getJSDocReturnType(decl)) : !decl.type && !!getJSDocType(decl); +} + +/** @internal */ +export function getContextNode(node: NamedDeclaration | BinaryExpression | ForInOrOfStatement | undefined): ContextNode | undefined { + if (!node) return undefined; + switch (node.kind) { + case SyntaxKind.VariableDeclaration: + return !isVariableDeclarationList(node.parent) || node.parent.declarations.length !== 1 ? + node : + isVariableStatement(node.parent.parent) ? + node.parent.parent : + isForInOrOfStatement(node.parent.parent) ? + getContextNode(node.parent.parent) : + node.parent; + + case SyntaxKind.BindingElement: + return getContextNode(node.parent.parent as NamedDeclaration); + + case SyntaxKind.ImportSpecifier: + return node.parent.parent.parent; + + case SyntaxKind.ExportSpecifier: + case SyntaxKind.NamespaceImport: + return node.parent.parent; + + case SyntaxKind.ImportClause: + case SyntaxKind.NamespaceExport: + return node.parent; + + case SyntaxKind.BinaryExpression: + return isExpressionStatement(node.parent) ? + node.parent : + node; + + case SyntaxKind.ForOfStatement: + case SyntaxKind.ForInStatement: + return { + start: (node as ForInOrOfStatement).initializer, + end: (node as ForInOrOfStatement).expression + }; + + case SyntaxKind.PropertyAssignment: + case SyntaxKind.ShorthandPropertyAssignment: + return isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent) ? + getContextNode( + findAncestor(node.parent, node => + isBinaryExpression(node) || isForInOrOfStatement(node) + ) as BinaryExpression | ForInOrOfStatement + ) : + node; + + default: + return node; + } +} + +/** @internal */ +export function toContextSpan(textSpan: TextSpan, sourceFile: SourceFile, context?: ContextNode): { contextSpan: TextSpan } | undefined { + if (!context) return undefined; + const contextSpan = isContextWithStartAndEndNode(context) ? getTextSpan(context.start, sourceFile, context.end) : getTextSpan(context, sourceFile); + return contextSpan.start !== textSpan.start || contextSpan.length !== textSpan.length ? { contextSpan } : undefined; +} + +/** @internal */ +export function isContextWithStartAndEndNode(node: ContextNode): node is ContextWithStartAndEndNode { + return node && (node as Node).kind === undefined; +} + +/** @internal */ +export function getTextSpan(node: Node, sourceFile: SourceFile, endNode?: Node): TextSpan { + let start = node.getStart(sourceFile); + let end = (endNode || node).getEnd(); + if (isStringLiteralLike(node) && (end - start) > 2) { + Debug.assert(endNode === undefined); + start += 1; + end -= 1; + } + return createTextSpanFromBounds(start, end); +} + +/** @internal */ +export function getTextSpanOfEntry(entry: Entry) { + return entry.kind === EntryKind.Span ? entry.textSpan : getTextSpan(entry.node, entry.node.getSourceFile()); +} + +/** @internal */ +export function getRangeOfEnclosingComment( + sourceFile: SourceFile, + position: number, + precedingToken?: Node | null, + tokenAtPosition = getTokenAtPosition(sourceFile, position), +): CommentRange | undefined { + const jsdoc = findAncestor(tokenAtPosition, isJSDoc); + if (jsdoc) tokenAtPosition = jsdoc.parent; + const tokenStart = tokenAtPosition.getStart(sourceFile); + if (tokenStart <= position && position < tokenAtPosition.getEnd()) { + return undefined; + } + + // eslint-disable-next-line no-null/no-null + precedingToken = precedingToken === null ? undefined : precedingToken === undefined ? findPrecedingToken(position, sourceFile) : precedingToken; + + // Between two consecutive tokens, all comments are either trailing on the former + // or leading on the latter (and none are in both lists). + const trailingRangesOfPreviousToken = precedingToken && getTrailingCommentRanges(sourceFile.text, precedingToken.end); + const leadingCommentRangesOfNextToken = getLeadingCommentRangesOfNode(tokenAtPosition, sourceFile); + const commentRanges = concatenate(trailingRangesOfPreviousToken, leadingCommentRangesOfNextToken); + return commentRanges && find(commentRanges, range => rangeContainsPositionExclusive(range, position) || + // The end marker of a single-line comment does not include the newline character. + // With caret at `^`, in the following case, we are inside a comment (^ denotes the cursor position): + // + // // asdf ^\n + // + // But for closed multi-line comments, we don't want to be inside the comment in the following case: + // + // /* asdf */^ + // + // However, unterminated multi-line comments *do* contain their end. + // + // Internally, we represent the end of the comment at the newline and closing '/', respectively. + // + position === range.end && (range.kind === SyntaxKind.SingleLineCommentTrivia || position === sourceFile.getFullWidth())); +} + +/** @internal */ +export function createTextRangeWithKind(pos: number, end: number, kind: T): TextRangeWithKind { + const textRangeWithKind: TextRangeWithKind = { pos, end, kind }; + if (Debug.isDebugging) { + Object.defineProperty(textRangeWithKind, "__debugKind", { + get: () => Debug.formatSyntaxKind(kind), + }); + } + return textRangeWithKind; +} + /** @internal */ export interface CaseClauseTracker { addValue(value: string | number): void; @@ -4218,4 +4421,12 @@ export function fileShouldUseJavaScriptRequire(file: SourceFile | string, progra } } return preferRequire; -} \ No newline at end of file +} + +/** @internal */ +export function getJSDocTypedefNode(node: Node): JSDocTypedefTag | undefined { + if (hasJSDocNodes(node)) { + return forEach(node.jsDoc, (node) => node.tags?.find(isJSDocTypedefTag)); + } + return undefined; +} diff --git a/src/testRunner/runner.ts b/src/testRunner/runner.ts index 7f2c5b77dc396..56bb5d9eb0820 100644 --- a/src/testRunner/runner.ts +++ b/src/testRunner/runner.ts @@ -234,11 +234,11 @@ function handleTestConfig() { } function beginTests() { - ts.Debug.loggingHost = { + ts.Debug.setLoggingHost({ log(_level, s) { console.log(s || ""); } - }; + }); if (ts.Debug.isDebugging) { ts.Debug.enableDebugInfo(); diff --git a/src/testRunner/unittests/debugDeprecation.ts b/src/testRunner/unittests/debugDeprecation.ts index 9dd3b0fee319f..d0e082aeaf432 100644 --- a/src/testRunner/unittests/debugDeprecation.ts +++ b/src/testRunner/unittests/debugDeprecation.ts @@ -2,12 +2,12 @@ import { deprecate } from "../../deprecatedCompat/deprecate"; import * as ts from "../_namespaces/ts"; describe("unittests:: debugDeprecation", () => { - let loggingHost: ts.LoggingHost | undefined; + let loggingHost: ts.Debug.LoggingHost | undefined; beforeEach(() => { loggingHost = ts.Debug.loggingHost; }); afterEach(() => { - ts.Debug.loggingHost = loggingHost; + ts.Debug.setLoggingHost(loggingHost); loggingHost = undefined; }); describe("deprecateFunction", () => { @@ -17,11 +17,11 @@ describe("unittests:: debugDeprecation", () => { typeScriptVersion: "3.8" }); let logWritten = false; - ts.Debug.loggingHost = { + ts.Debug.setLoggingHost({ log() { logWritten = true; } - }; + }); deprecation(); assert.isFalse(logWritten); }); @@ -31,11 +31,11 @@ describe("unittests:: debugDeprecation", () => { typeScriptVersion: "3.9" }); let logWritten = false; - ts.Debug.loggingHost = { + ts.Debug.setLoggingHost({ log() { logWritten = true; } - }; + }); deprecation(); assert.isTrue(logWritten); }); @@ -44,11 +44,11 @@ describe("unittests:: debugDeprecation", () => { typeScriptVersion: "3.9" }); let logWritten = false; - ts.Debug.loggingHost = { + ts.Debug.setLoggingHost({ log() { logWritten = true; } - }; + }); deprecation(); assert.isTrue(logWritten); }); @@ -57,11 +57,11 @@ describe("unittests:: debugDeprecation", () => { typeScriptVersion: "3.9" }); let logWrites = 0; - ts.Debug.loggingHost = { + ts.Debug.setLoggingHost({ log() { logWrites++; } - }; + }); deprecation(); deprecation(); assert.equal(logWrites, 1); @@ -73,11 +73,11 @@ describe("unittests:: debugDeprecation", () => { typeScriptVersion: "3.9" }); let logWritten = false; - ts.Debug.loggingHost = { + ts.Debug.setLoggingHost({ log() { logWritten = true; } - }; + }); expect(deprecation).throws(); assert.isFalse(logWritten); }); @@ -86,11 +86,11 @@ describe("unittests:: debugDeprecation", () => { error: true, }); let logWritten = false; - ts.Debug.loggingHost = { + ts.Debug.setLoggingHost({ log() { logWritten = true; } - }; + }); expect(deprecation).throws(); assert.isFalse(logWritten); }); diff --git a/src/testRunner/unittests/helpers/virtualFileSystemWithWatch.ts b/src/testRunner/unittests/helpers/virtualFileSystemWithWatch.ts index 0ab3df9d2ab4e..a88c99670039c 100644 --- a/src/testRunner/unittests/helpers/virtualFileSystemWithWatch.ts +++ b/src/testRunner/unittests/helpers/virtualFileSystemWithWatch.ts @@ -1,3 +1,5 @@ +import { matchFiles } from "../../../compiler/fileMatcher"; +import { ModuleImportResult } from "../../../server/types"; import * as Harness from "../../_namespaces/Harness"; import { clear, @@ -30,8 +32,6 @@ import { isArray, isString, mapDefined, - matchFiles, - ModuleImportResult, ModuleResolutionHost, MultiMap, noop, diff --git a/src/testRunner/unittests/parsePseudoBigInt.ts b/src/testRunner/unittests/parsePseudoBigInt.ts index 10f5e098362f9..cadf1f566f584 100644 --- a/src/testRunner/unittests/parsePseudoBigInt.ts +++ b/src/testRunner/unittests/parsePseudoBigInt.ts @@ -1,4 +1,4 @@ -import * as ts from "../_namespaces/ts"; +import { parsePseudoBigInt } from "../../compiler/scannerUtilities"; describe("unittests:: BigInt literal base conversions", () => { describe("parsePseudoBigInt", () => { @@ -11,7 +11,7 @@ describe("unittests:: BigInt literal base conversions", () => { for (const testNumber of testNumbers) { for (let leadingZeros = 0; leadingZeros < 10; leadingZeros++) { assert.equal( - ts.parsePseudoBigInt("0".repeat(leadingZeros) + testNumber + "n"), + parsePseudoBigInt("0".repeat(leadingZeros) + testNumber + "n"), String(testNumber) ); } @@ -22,7 +22,7 @@ describe("unittests:: BigInt literal base conversions", () => { for (let leadingZeros = 0; leadingZeros < 10; leadingZeros++) { const binary = "0".repeat(leadingZeros) + testNumber.toString(2) + "n"; for (const prefix of ["0b", "0B"]) { - assert.equal(ts.parsePseudoBigInt(prefix + binary), String(testNumber)); + assert.equal(parsePseudoBigInt(prefix + binary), String(testNumber)); } } } @@ -32,7 +32,7 @@ describe("unittests:: BigInt literal base conversions", () => { for (let leadingZeros = 0; leadingZeros < 10; leadingZeros++) { const octal = "0".repeat(leadingZeros) + testNumber.toString(8) + "n"; for (const prefix of ["0o", "0O"]) { - assert.equal(ts.parsePseudoBigInt(prefix + octal), String(testNumber)); + assert.equal(parsePseudoBigInt(prefix + octal), String(testNumber)); } } } @@ -43,7 +43,7 @@ describe("unittests:: BigInt literal base conversions", () => { const hex = "0".repeat(leadingZeros) + testNumber.toString(16) + "n"; for (const prefix of ["0x", "0X"]) { for (const hexCase of [hex.toLowerCase(), hex.toUpperCase()]) { - assert.equal(ts.parsePseudoBigInt(prefix + hexCase), String(testNumber)); + assert.equal(parsePseudoBigInt(prefix + hexCase), String(testNumber)); } } } @@ -51,19 +51,19 @@ describe("unittests:: BigInt literal base conversions", () => { }); it("can parse large literals", () => { assert.equal( - ts.parsePseudoBigInt("123456789012345678901234567890n"), + parsePseudoBigInt("123456789012345678901234567890n"), "123456789012345678901234567890" ); assert.equal( - ts.parsePseudoBigInt("0b1100011101110100100001111111101101100001101110011111000001110111001001110001111110000101011010010n"), + parsePseudoBigInt("0b1100011101110100100001111111101101100001101110011111000001110111001001110001111110000101011010010n"), "123456789012345678901234567890" ); assert.equal( - ts.parsePseudoBigInt("0o143564417755415637016711617605322n"), + parsePseudoBigInt("0o143564417755415637016711617605322n"), "123456789012345678901234567890" ); assert.equal( - ts.parsePseudoBigInt("0x18ee90ff6c373e0ee4e3f0ad2n"), + parsePseudoBigInt("0x18ee90ff6c373e0ee4e3f0ad2n"), "123456789012345678901234567890" ); }); diff --git a/src/testRunner/unittests/services/convertToAsyncFunction.ts b/src/testRunner/unittests/services/convertToAsyncFunction.ts index 00b46896aaaa2..b7e857a8c5af8 100644 --- a/src/testRunner/unittests/services/convertToAsyncFunction.ts +++ b/src/testRunner/unittests/services/convertToAsyncFunction.ts @@ -1,15 +1,10 @@ +import { getFormatContext } from "../../../services/formatting/formatting"; +import { applyChanges } from "../../../services/textChanges"; import * as Harness from "../../_namespaces/Harness"; import * as ts from "../../_namespaces/ts"; import { createProjectService } from "../helpers/tsserver"; -import { - createServerHost, - File, -} from "../helpers/virtualFileSystemWithWatch"; -import { - extractTest, - newLineCharacter, - notImplementedHost, -} from "./extract/helpers"; +import { createServerHost, File } from "../helpers/virtualFileSystemWithWatch"; +import { extractTest, newLineCharacter, notImplementedHost } from "./extract/helpers"; const libFile: File = { path: "/a/lib/lib.d.ts", @@ -356,7 +351,7 @@ function testConvertToAsyncFunction(it: Mocha.PendingTestFunction, caption: stri cancellationToken: { throwIfCancellationRequested: ts.noop, isCancellationRequested: ts.returnFalse }, preferences: ts.emptyOptions, host: notImplementedHost, - formatContext: ts.formatting.getFormatContext(ts.testFormatSettings, notImplementedHost) + formatContext: getFormatContext(ts.testFormatSettings, notImplementedHost) }; const diagnostics = languageService.getSuggestionDiagnostics(f.path); @@ -374,7 +369,7 @@ function testConvertToAsyncFunction(it: Mocha.PendingTestFunction, caption: stri assert.lengthOf(changes, 1); data.push(`// ==ASYNC FUNCTION::${action.description}==`); - const newText = ts.textChanges.applyChanges(sourceFile.text, changes[0].textChanges); + const newText = applyChanges(sourceFile.text, changes[0].textChanges); data.push(newText); const diagProgram = makeLanguageService({ path, content: newText }, includeLib, includeModule).getProgram()!; diff --git a/src/testRunner/unittests/services/extract/helpers.ts b/src/testRunner/unittests/services/extract/helpers.ts index cc139c3f42a2c..d73c3f6231184 100644 --- a/src/testRunner/unittests/services/extract/helpers.ts +++ b/src/testRunner/unittests/services/extract/helpers.ts @@ -1,3 +1,5 @@ +import { getFormatContext } from "../../../../services/formatting/formatting"; +import { applyChanges } from "../../../../services/textChanges"; import * as Harness from "../../../_namespaces/Harness"; import * as ts from "../../../_namespaces/ts"; import { createProjectService } from "../../helpers/tsserver"; @@ -111,7 +113,7 @@ export function testExtractSymbol(caption: string, text: string, baselineFolder: startPosition: selectionRange.pos, endPosition: selectionRange.end, host: notImplementedHost, - formatContext: ts.formatting.getFormatContext(ts.testFormatSettings, notImplementedHost), + formatContext: getFormatContext(ts.testFormatSettings, notImplementedHost), preferences: ts.emptyOptions, }; const rangeToExtract = ts.refactor.extractSymbol.getRangeToExtract(sourceFile, ts.createTextSpanFromRange(selectionRange)); @@ -126,7 +128,7 @@ export function testExtractSymbol(caption: string, text: string, baselineFolder: const { renameLocation, edits } = ts.refactor.extractSymbol.getRefactorEditsToExtractSymbol(context, action.name)!; assert.lengthOf(edits, 1); data.push(`// ==SCOPE::${action.description}==`); - const newText = ts.textChanges.applyChanges(sourceFile.text, edits[0].textChanges); + const newText = applyChanges(sourceFile.text, edits[0].textChanges); const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation); data.push(newTextWithRename); @@ -174,7 +176,7 @@ export function testExtractSymbolFailed(caption: string, text: string, descripti startPosition: selectionRange.pos, endPosition: selectionRange.end, host: notImplementedHost, - formatContext: ts.formatting.getFormatContext(ts.testFormatSettings, notImplementedHost), + formatContext: getFormatContext(ts.testFormatSettings, notImplementedHost), preferences: ts.emptyOptions, }; const rangeToExtract = ts.refactor.extractSymbol.getRangeToExtract(sourceFile, ts.createTextSpanFromRange(selectionRange)); diff --git a/src/testRunner/unittests/services/organizeImports.ts b/src/testRunner/unittests/services/organizeImports.ts index 9794bd1d68746..7bd8d47734803 100644 --- a/src/testRunner/unittests/services/organizeImports.ts +++ b/src/testRunner/unittests/services/organizeImports.ts @@ -1,3 +1,4 @@ +import { applyChanges } from "../../../services/textChanges"; import * as Harness from "../../_namespaces/Harness"; import * as ts from "../../_namespaces/ts"; import { createProjectService } from "../helpers/tsserver"; @@ -1025,7 +1026,7 @@ export * from "lib"; assert.equal(changes.length, 1); assert.equal(changes[0].fileName, testPath); - const newText = ts.textChanges.applyChanges(testContent, changes[0].textChanges); + const newText = applyChanges(testContent, changes[0].textChanges); Harness.Baseline.runBaseline(baselinePath, [ "// ==ORIGINAL==", testContent, diff --git a/src/testRunner/unittests/services/textChanges.ts b/src/testRunner/unittests/services/textChanges.ts index 0b45e947a2e55..441153f8306c8 100644 --- a/src/testRunner/unittests/services/textChanges.ts +++ b/src/testRunner/unittests/services/textChanges.ts @@ -1,3 +1,7 @@ +import { getNewLineCharacter } from "../../../compiler/utilities"; +import { getFormatContext } from "../../../services/formatting/formatting"; +import { applyChanges, ChangeTracker, deleteNode, LeadingTriviaOption, TrailingTriviaOption } from "../../../services/textChanges"; +import { FormatContext } from "../../../services/types"; import * as Harness from "../../_namespaces/Harness"; import * as ts from "../../_namespaces/ts"; import { notImplementedHost } from "./extract/helpers"; @@ -19,10 +23,10 @@ describe("unittests:: services:: textChanges", () => { } const printerOptions = { newLine: ts.NewLineKind.LineFeed }; - const newLineCharacter = ts.getNewLineCharacter(printerOptions); + const newLineCharacter = getNewLineCharacter(printerOptions); - function getRuleProvider(placeOpenBraceOnNewLineForFunctions: boolean): ts.formatting.FormatContext { - return ts.formatting.getFormatContext(placeOpenBraceOnNewLineForFunctions ? { ...ts.testFormatSettings, placeOpenBraceOnNewLineForFunctions: true } : ts.testFormatSettings, notImplementedHost); + function getRuleProvider(placeOpenBraceOnNewLineForFunctions: boolean): FormatContext { + return getFormatContext(placeOpenBraceOnNewLineForFunctions ? { ...ts.testFormatSettings, placeOpenBraceOnNewLineForFunctions: true } : ts.testFormatSettings, notImplementedHost); } // validate that positions that were recovered from the printed text actually match positions that will be created if the same text is parsed. @@ -47,16 +51,16 @@ describe("unittests:: services:: textChanges", () => { } } - function runSingleFileTest(caption: string, placeOpenBraceOnNewLineForFunctions: boolean, text: string, validateNodes: boolean, testBlock: (sourceFile: ts.SourceFile, changeTracker: ts.textChanges.ChangeTracker) => void) { + function runSingleFileTest(caption: string, placeOpenBraceOnNewLineForFunctions: boolean, text: string, validateNodes: boolean, testBlock: (sourceFile: ts.SourceFile, changeTracker: ChangeTracker) => void) { it(caption, () => { const sourceFile = ts.createSourceFile("source.ts", text, ts.ScriptTarget.ES2015, /*setParentNodes*/ true); const rulesProvider = getRuleProvider(placeOpenBraceOnNewLineForFunctions); - const changeTracker = new ts.textChanges.ChangeTracker(newLineCharacter, rulesProvider); + const changeTracker = new ChangeTracker(newLineCharacter, rulesProvider); testBlock(sourceFile, changeTracker); const changes = changeTracker.getChanges(validateNodes ? verifyPositions : undefined); assert.equal(changes.length, 1); assert.equal(changes[0].fileName, sourceFile.fileName); - const modified = ts.textChanges.applyChanges(sourceFile.text, changes[0].textChanges); + const modified = applyChanges(sourceFile.text, changes[0].textChanges); Harness.Baseline.runBaseline(`textChanges/${caption}.js`, `===ORIGINAL===${newLineCharacter}${text}${newLineCharacter}===MODIFIED===${newLineCharacter}${modified}`); }); } @@ -127,7 +131,6 @@ function bar() { function findVariableDeclarationContaining(name: string, sourceFile: ts.SourceFile): ts.VariableDeclaration { return ts.cast(findChild(name, sourceFile), ts.isVariableDeclaration); } - const { deleteNode } = ts.textChanges; { const text = ` var x = 1; // some comment - 1 @@ -141,13 +144,13 @@ var z = 3; // comment 4 deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile)); }); runSingleFileTest("deleteNode2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude }); + deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { leadingTriviaOption: LeadingTriviaOption.Exclude }); }); runSingleFileTest("deleteNode3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude }); + deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { trailingTriviaOption: TrailingTriviaOption.Exclude }); }); runSingleFileTest("deleteNode4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { - deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude }); + deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { leadingTriviaOption: LeadingTriviaOption.Exclude, trailingTriviaOption: TrailingTriviaOption.Exclude }); }); runSingleFileTest("deleteNode5", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { deleteNode(changeTracker, sourceFile, findVariableStatementContaining("x", sourceFile)); @@ -168,15 +171,15 @@ var a = 4; // comment 7 }); runSingleFileTest("deleteNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), - { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude }); + { leadingTriviaOption: LeadingTriviaOption.Exclude }); }); runSingleFileTest("deleteNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), - { trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude }); + { trailingTriviaOption: TrailingTriviaOption.Exclude }); }); runSingleFileTest("deleteNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => { changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), - { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude }); + { leadingTriviaOption: LeadingTriviaOption.Exclude, trailingTriviaOption: TrailingTriviaOption.Exclude }); }); } function createTestVariableDeclaration(name: string) { @@ -253,16 +256,16 @@ var a = 4; // comment 7`; changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { suffix: newLineCharacter }); }); runSingleFileTest("replaceNode2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { leadingTriviaOption: LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter }); }); runSingleFileTest("replaceNode3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { trailingTriviaOption: TrailingTriviaOption.Exclude, suffix: newLineCharacter }); }); runSingleFileTest("replaceNode4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { leadingTriviaOption: LeadingTriviaOption.Exclude, trailingTriviaOption: TrailingTriviaOption.Exclude }); }); runSingleFileTest("replaceNode5", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNode(sourceFile, findVariableStatementContaining("x", sourceFile), createTestClass(), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude }); + changeTracker.replaceNode(sourceFile, findVariableStatementContaining("x", sourceFile), createTestClass(), { leadingTriviaOption: LeadingTriviaOption.Exclude, trailingTriviaOption: TrailingTriviaOption.Exclude }); }); } { @@ -278,13 +281,13 @@ var a = 4; // comment 7`; changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { suffix: newLineCharacter }); }); runSingleFileTest("replaceNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter }); + changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { leadingTriviaOption: LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter }); }); runSingleFileTest("replaceNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter }); + changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { trailingTriviaOption: TrailingTriviaOption.Exclude, suffix: newLineCharacter }); }); runSingleFileTest("replaceNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => { - changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude }); + changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { leadingTriviaOption: LeadingTriviaOption.Exclude, trailingTriviaOption: TrailingTriviaOption.Exclude }); }); } { diff --git a/src/testRunner/unittests/tsserver/exportMapCache.ts b/src/testRunner/unittests/tsserver/exportMapCache.ts index 601be84a76f4a..c6c50ff31c072 100644 --- a/src/testRunner/unittests/tsserver/exportMapCache.ts +++ b/src/testRunner/unittests/tsserver/exportMapCache.ts @@ -1,3 +1,4 @@ +import { getSymbolId } from "../../../compiler/utilities"; import * as ts from "../../_namespaces/ts"; import { baselineTsserverLogs, @@ -108,7 +109,7 @@ describe("unittests:: tsserver:: exportMapCache", () => { }); assert.ok(sigintPropBefore); assert.ok(sigintPropBefore![0].symbol.flags & ts.SymbolFlags.Transient); - const symbolIdBefore = ts.getSymbolId(sigintPropBefore![0].symbol); + const symbolIdBefore = getSymbolId(sigintPropBefore![0].symbol); // Update program without clearing cache session.executeCommandSeq({ @@ -133,7 +134,7 @@ describe("unittests:: tsserver:: exportMapCache", () => { if (symbolName === "SIGINT") sigintPropAfter = info; }); assert.ok(sigintPropAfter); - assert.notEqual(symbolIdBefore, ts.getSymbolId(sigintPropAfter![0].symbol)); + assert.notEqual(symbolIdBefore, getSymbolId(sigintPropAfter![0].symbol)); baselineTsserverLogs("exportMapCache", "does not store transient symbols through program updates", session); }); diff --git a/src/testRunner/unittests/tsserver/symlinkCache.ts b/src/testRunner/unittests/tsserver/symlinkCache.ts index 4729f2c69a2ce..05b6c3a87ced7 100644 --- a/src/testRunner/unittests/tsserver/symlinkCache.ts +++ b/src/testRunner/unittests/tsserver/symlinkCache.ts @@ -1,3 +1,6 @@ +import { + createSymlinkCache, +} from "../../../compiler/symlinkCache"; import * as ts from "../../_namespaces/ts"; import { baselineTsserverLogs, @@ -72,7 +75,7 @@ describe("unittests:: tsserver:: symlinkCache", () => { }); it("works for paths close to the root", () => { - const cache = ts.createSymlinkCache("/", ts.createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ false)); + const cache = createSymlinkCache("/", ts.createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ false)); // Used to crash, #44953 const map = ts.createModeAwareCache(); map.set("foo", /*mode*/ undefined, { diff --git a/src/tsc/tsc.ts b/src/tsc/tsc.ts index ce8b356b685d9..b4b09d1f96132 100644 --- a/src/tsc/tsc.ts +++ b/src/tsc/tsc.ts @@ -1,24 +1,27 @@ -import * as ts from "./_namespaces/ts"; +import { noop } from "../compiler/core"; +import * as Debug from "../compiler/debug"; +import { sys } from "../compiler/sys"; +import { executeCommandLine } from "../executeCommandLine/executeCommandLine"; // This file actually uses arguments passed on commandline and executes it // enable deprecation logging -ts.Debug.loggingHost = { +Debug.setLoggingHost({ log(_level, s) { - ts.sys.write(`${s || ""}${ts.sys.newLine}`); + sys.write(`${s || ""}${sys.newLine}`); } -}; +}); -if (ts.Debug.isDebugging) { - ts.Debug.enableDebugInfo(); +if (Debug.isDebugging) { + Debug.enableDebugInfo(); } -if (ts.sys.tryEnableSourceMapsForHost && /^development$/i.test(ts.sys.getEnvironmentVariable("NODE_ENV"))) { - ts.sys.tryEnableSourceMapsForHost(); +if (sys.tryEnableSourceMapsForHost && /^development$/i.test(sys.getEnvironmentVariable("NODE_ENV"))) { + sys.tryEnableSourceMapsForHost(); } -if (ts.sys.setBlocking) { - ts.sys.setBlocking(); +if (sys.setBlocking) { + sys.setBlocking(); } -ts.executeCommandLine(ts.sys, ts.noop, ts.sys.args); +executeCommandLine(sys, noop, sys.args); diff --git a/src/tsserver/common.ts b/src/tsserver/common.ts index 76ff50afed512..845a454055bd3 100644 --- a/src/tsserver/common.ts +++ b/src/tsserver/common.ts @@ -1,10 +1,12 @@ -import { LanguageServiceMode } from "./_namespaces/ts"; import { - Logger, - LogLevel, ServerCancellationToken, SessionOptions, -} from "./_namespaces/ts.server"; +} from "../server/session"; +import { + Logger, + LogLevel, +} from "../server/utilitiesPublic"; +import { LanguageServiceMode } from "../services/types"; /** @internal */ export function getLogLevel(level: string | undefined) { diff --git a/src/tsserver/nodeServer.ts b/src/tsserver/nodeServer.ts index e33a94bce796f..a23fa349c4b64 100644 --- a/src/tsserver/nodeServer.ts +++ b/src/tsserver/nodeServer.ts @@ -1,80 +1,107 @@ -import * as protocol from "../server/protocol"; -import * as ts from "./_namespaces/ts"; import { - ApplyCodeActionCommandResult, assertType, - CharacterCodes, - combinePaths, createQueue, - Debug, + noop, + toFileNameLowerCase, +} from "../compiler/core"; +import { + MapLike, + SortedReadonlyArray, + versionMajorMinor, +} from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { + combinePaths, directorySeparator, - DirectoryWatcherCallback, - FileWatcher, getDirectoryPath, getRootLength, - JsTyping, - LanguageServiceMode, - MapLike, - noop, - noopFileWatcher, normalizePath, normalizeSlashes, - perfLogger, - SortedReadonlyArray, +} from "../compiler/path"; +import { perfLogger } from "../compiler/perfLogger"; +import { sys as system } from "../compiler/sys"; +import { startTracing, - stripQuotes, - sys, - toFileNameLowerCase, tracing, +} from "../compiler/tracing"; +import { + CharacterCodes, + DirectoryWatcherCallback, + FileWatcher, TypeAcquisition, - validateLocaleAndSetLanguage, - versionMajorMinor, WatchOptions, -} from "./_namespaces/ts"; -import * as server from "./_namespaces/ts.server"; +} from "../compiler/types"; +import { stripQuotes } from "../compiler/utilities"; +import { validateLocaleAndSetLanguage } from "../compiler/utilitiesPublic"; +import { noopFileWatcher } from "../compiler/watch"; +import { + NameValidationResult, + validatePackageName, +} from "../jsTyping/jsTyping"; import { ActionInvalidate, ActionPackageInstalled, ActionSet, ActionWatchTypingLocations, Arguments, - BeginInstallTypes, - createInstallTypingsRequest, - EndInstallTypes, EventBeginInstallTypes, EventEndInstallTypes, EventInitializationFailed, EventTypesRegistry, findArgument, - formatMessage, - getLogLevel, hasArgument, - indent, + nowString, +} from "../jsTyping/shared"; +import { + BeginInstallTypes, + EndInstallTypes, InitializationFailedResponse, - InstallPackageOptionsWithProject, InstallPackageRequest, InvalidateCachedTypings, + PackageInstalledResponse, + SetTypings, + TypesRegistryResponse, + TypingInstallerRequestUnion, + WatchTypingLocations, +} from "../jsTyping/types"; +import { ProjectService } from "../server/editorServices"; +import { Project } from "../server/project"; +import * as protocol from "../server/protocol"; +import { + Event, + formatMessage, + nullCancellationToken, + ServerCancellationToken, + Session, + toEvent, +} from "../server/session"; +import { + ServerHost, +} from "../server/types"; +import { + InstallPackageOptionsWithProject, ITypingsInstaller, + nullTypingsInstaller, +} from "../server/typingsCache"; +import { + indent, + stringifyIndented, +} from "../server/utilities"; +import { + createInstallTypingsRequest, Logger, LogLevel, Msg, - nowString, - nullCancellationToken, - nullTypingsInstaller, - PackageInstalledResponse, - Project, - ProjectService, - ServerCancellationToken, - ServerHost, - Session, - SetTypings, +} from "../server/utilitiesPublic"; +import { + ApplyCodeActionCommandResult, + LanguageServiceMode, +} from "../services/types"; +import { + getLogLevel, StartInput, StartSessionOptions, - stringifyIndented, - toEvent, - TypesRegistryResponse, - TypingInstallerRequestUnion, -} from "./_namespaces/ts.server"; +} from "./common"; interface LogOptions { file?: string; @@ -166,7 +193,7 @@ function parseServerMode(): LanguageServiceMode | string | undefined { /** @internal */ export function initializeNodeSystem(): StartInput { - const sys = Debug.checkDefined(ts.sys) as ServerHost; + const sys = Debug.checkDefined(system) as ServerHost; const childProcess: { execFileSync(file: string, args: string[], options: { stdio: "ignore", env: MapLike }): string | Buffer; } = require("child_process"); @@ -302,18 +329,18 @@ export function initializeNodeSystem(): StartInput { const logger = createLogger(); // enable deprecation logging - Debug.loggingHost = { + Debug.setLoggingHost({ log(level, s) { switch (level) { - case ts.LogLevel.Error: - case ts.LogLevel.Warning: + case Debug.LogLevel.Error: + case Debug.LogLevel.Warning: return logger.msg(s, Msg.Err); - case ts.LogLevel.Info: - case ts.LogLevel.Verbose: + case Debug.LogLevel.Info: + case Debug.LogLevel.Verbose: return logger.msg(s, Msg.Info); } } - }; + }); const pending = createQueue(); let canWrite = true; @@ -551,13 +578,13 @@ function startNodeSession(options: StartSessionOptions, logger: Logger, cancella readonly typesMapLocation: string, private readonly npmLocation: string | undefined, private readonly validateDefaultNpmLocation: boolean, - private event: server.Event) { + private event: Event) { } isKnownTypesPackageName(name: string): boolean { // We want to avoid looking this up in the registry as that is expensive. So first check that it's actually an NPM package. - const validationResult = JsTyping.validatePackageName(name); - if (validationResult !== JsTyping.NameValidationResult.Ok) { + const validationResult = validatePackageName(name); + if (validationResult !== NameValidationResult.Ok) { return false; } @@ -618,7 +645,7 @@ function startNodeSession(options: StartSessionOptions, logger: Logger, cancella } } - const typingsInstaller = combinePaths(getDirectoryPath(sys.getExecutingFilePath()), "typingsInstaller.js"); + const typingsInstaller = combinePaths(getDirectoryPath(system.getExecutingFilePath()), "typingsInstaller.js"); this.installer = childProcess.fork(typingsInstaller, args, { execArgv }); this.installer.on("message", m => this.handleMessage(m)); @@ -670,7 +697,7 @@ function startNodeSession(options: StartSessionOptions, logger: Logger, cancella } } - private handleMessage(response: TypesRegistryResponse | PackageInstalledResponse | SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse | server.WatchTypingLocations) { + private handleMessage(response: TypesRegistryResponse | PackageInstalledResponse | SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse | WatchTypingLocations) { if (this.logger.hasLevel(LogLevel.verbose)) { this.logger.info(`Received response:${stringifyIndented(response)}`); } @@ -795,7 +822,7 @@ function startNodeSession(options: StartSessionOptions, logger: Logger, cancella this.event(body, eventName); }; - const host = sys as ServerHost; + const host = system as ServerHost; const typingsInstaller = disableAutomaticTypingAcquisition ? undefined @@ -907,7 +934,7 @@ function startNodeSession(options: StartSessionOptions, logger: Logger, cancella const eventPort: number | undefined = parseEventPort(findArgument("--eventPort")); const typingSafeListLocation = findArgument(Arguments.TypingSafeListLocation)!; // TODO: GH#18217 - const typesMapLocation = findArgument(Arguments.TypesMapLocation) || combinePaths(getDirectoryPath(sys.getExecutingFilePath()), "typesMap.json"); + const typesMapLocation = findArgument(Arguments.TypesMapLocation) || combinePaths(getDirectoryPath(system.getExecutingFilePath()), "typesMap.json"); const npmLocation = findArgument(Arguments.NpmLocation); const validateDefaultNpmLocation = hasArgument(Arguments.ValidateDefaultNpmLocation); const disableAutomaticTypingAcquisition = hasArgument("--disableAutomaticTypingAcquisition"); diff --git a/src/tsserver/server.ts b/src/tsserver/server.ts index f752178022f08..095d5e5fd7fb9 100644 --- a/src/tsserver/server.ts +++ b/src/tsserver/server.ts @@ -1,17 +1,19 @@ +import { version } from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; import { - Debug, setStackTraceLimit, sys, - version, -} from "./_namespaces/ts"; +} from "../compiler/sys"; import { - emptyArray, findArgument, hasArgument, - initializeNodeSystem, +} from "../jsTyping/shared"; +import { + emptyArray, Msg, - StartInput, -} from "./_namespaces/ts.server"; +} from "../server/utilitiesPublic"; +import { StartInput } from "./common"; +import { initializeNodeSystem } from "./nodeServer"; export * from "./_namespaces/ts"; @@ -23,7 +25,6 @@ function findArgumentStringArray(argName: string): readonly string[] { return arg.split(",").filter(name => name !== ""); } - function start({ args, logger, cancellationToken, serverMode, unknownServerMode, startSession: startServer }: StartInput, platform: string) { logger.info(`Starting TS Server`); diff --git a/src/typescript/typescript.ts b/src/typescript/typescript.ts index 9f1934f652a2c..510cca1ecd8b2 100644 --- a/src/typescript/typescript.ts +++ b/src/typescript/typescript.ts @@ -1,22 +1,20 @@ -import { - Debug, - LogLevel, -} from "./_namespaces/ts"; +import * as Debug from "../compiler/debug"; import * as ts from "./_namespaces/ts"; // enable deprecation logging declare const console: any; + if (typeof console !== "undefined") { - Debug.loggingHost = { + Debug.setLoggingHost({ log(level, s) { switch (level) { - case LogLevel.Error: return console.error(s); - case LogLevel.Warning: return console.warn(s); - case LogLevel.Info: return console.log(s); - case LogLevel.Verbose: return console.log(s); + case Debug.LogLevel.Error: return console.error(s); + case Debug.LogLevel.Warning: return console.warn(s); + case Debug.LogLevel.Info: return console.log(s); + case Debug.LogLevel.Verbose: return console.log(s); } } - }; + }); } export = ts; diff --git a/src/typingsInstaller/nodeTypingsInstaller.ts b/src/typingsInstaller/nodeTypingsInstaller.ts index 91be7a4b8c641..71cb603209584 100644 --- a/src/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/typingsInstaller/nodeTypingsInstaller.ts @@ -2,39 +2,45 @@ import * as fs from "fs"; import * as path from "path"; import { - combinePaths, createGetCanonicalFileName, - Debug, + stringContains, +} from "../compiler/core"; +import { + MapLike, + version, +} from "../compiler/corePublic"; +import * as Debug from "../compiler/debug"; +import { + combinePaths, forEachAncestorDirectory, getDirectoryPath, - MapLike, normalizePath, normalizeSlashes, - stringContains, - sys, toPath, - version, -} from "./_namespaces/ts"; +} from "../compiler/path"; +import { sys } from "../compiler/sys"; import { ActionPackageInstalled, Arguments, EventTypesRegistry, findArgument, hasArgument, + nowString, +} from "../jsTyping/shared"; +import { InitializationFailedResponse, InstallTypingHost, - nowString, PackageInstalledResponse, TypesRegistryResponse, TypingInstallerRequestUnion, TypingInstallerResponseUnion, -} from "./_namespaces/ts.server"; +} from "../jsTyping/types"; import { installNpmPackages, Log, RequestCompletedAction, TypingsInstaller, -} from "./_namespaces/ts.server.typingsInstaller"; +} from "../typingsInstallerCore/typingsInstaller"; class FileLog implements Log { constructor(private logFile: string | undefined) { diff --git a/src/typingsInstallerCore/typingsInstaller.ts b/src/typingsInstallerCore/typingsInstaller.ts index 71d6f2dff9da2..ada5bdfb9aaaa 100644 --- a/src/typingsInstallerCore/typingsInstaller.ts +++ b/src/typingsInstallerCore/typingsInstaller.ts @@ -1,36 +1,58 @@ import { - combinePaths, - forEachKey, - getBaseFileName, - getDirectoryPath, getProperty, hasProperty, - JsTyping, - mangleScopedPackageName, mapDefined, - MapLike, - ModuleResolutionKind, noop, - Path, - resolveModuleName, - Version, +} from "../compiler/core"; +import { + MapLike, version, versionMajorMinor, -} from "./_namespaces/ts"; +} from "../compiler/corePublic"; +import { + mangleScopedPackageName, + resolveModuleName, +} from "../compiler/moduleNameResolver"; +import { + combinePaths, + getBaseFileName, + getDirectoryPath, +} from "../compiler/path"; +import { Version } from "../compiler/semver"; +import { + ModuleResolutionKind, + Path, +} from "../compiler/types"; +import { + forEachKey, +} from "../compiler/utilities"; +import { + CachedTyping, + discoverTypings, + isTypingUpToDate, + loadSafeList, + loadTypesMap, + NameValidationResult, + renderPackageNameValidationFailure, + SafeList, + validatePackageName, +} from "../jsTyping/jsTyping"; import { ActionSet, ActionWatchTypingLocations, + EventBeginInstallTypes, + EventEndInstallTypes, +} from "../jsTyping/shared"; +import { BeginInstallTypes, CloseProject, DiscoverTypings, EndInstallTypes, - EventBeginInstallTypes, - EventEndInstallTypes, InstallTypingHost, InvalidateCachedTypings, SetTypings, WatchTypingLocations, -} from "./_namespaces/ts.server"; +} from "../jsTyping/types"; interface NpmConfig { devDependencies: MapLike; @@ -99,11 +121,11 @@ export interface PendingRequest { } export abstract class TypingsInstaller { - private readonly packageNameToTypingLocation = new Map(); + private readonly packageNameToTypingLocation = new Map(); private readonly missingTypingsSet = new Set(); private readonly knownCachesSet = new Set(); private readonly projectWatchers = new Map>(); - private safeList: JsTyping.SafeList | undefined; + private safeList: SafeList | undefined; /** @internal */ readonly pendingRunRequests: PendingRequest[] = []; @@ -167,7 +189,7 @@ export abstract class TypingsInstaller { if (this.safeList === undefined) { this.initializeSafeList(); } - const discoverTypingsResult = JsTyping.discoverTypings( + const discoverTypingsResult = discoverTypings( this.installTypingHost, this.log.isEnabled() ? (s => this.log.writeLine(s)) : undefined, req.fileNames, @@ -201,7 +223,7 @@ export abstract class TypingsInstaller { private initializeSafeList() { // Prefer the safe list from the types map if it exists if (this.typesMapLocation) { - const safeListFromMap = JsTyping.loadTypesMap(this.installTypingHost, this.typesMapLocation); + const safeListFromMap = loadTypesMap(this.installTypingHost, this.typesMapLocation); if (safeListFromMap) { this.log.writeLine(`Loaded safelist from types map file '${this.typesMapLocation}'`); this.safeList = safeListFromMap; @@ -209,7 +231,7 @@ export abstract class TypingsInstaller { } this.log.writeLine(`Failed to load safelist from types map file '${this.typesMapLocation}'`); } - this.safeList = JsTyping.loadSafeList(this.installTypingHost, this.safeListPath); + this.safeList = loadSafeList(this.installTypingHost, this.safeListPath); } private processCacheLocation(cacheLocation: string) { @@ -269,7 +291,7 @@ export abstract class TypingsInstaller { continue; } - const newTyping: JsTyping.CachedTyping = { typingLocation: typingFile, version: new Version(version) }; + const newTyping: CachedTyping = { typingLocation: typingFile, version: new Version(version) }; this.packageNameToTypingLocation.set(packageName, newTyping); } } @@ -287,18 +309,18 @@ export abstract class TypingsInstaller { if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: '${typingKey}' is in missingTypingsSet - skipping...`); return undefined; } - const validationResult = JsTyping.validatePackageName(typing); - if (validationResult !== JsTyping.NameValidationResult.Ok) { + const validationResult = validatePackageName(typing); + if (validationResult !== NameValidationResult.Ok) { // add typing name to missing set so we won't process it again this.missingTypingsSet.add(typingKey); - if (this.log.isEnabled()) this.log.writeLine(JsTyping.renderPackageNameValidationFailure(validationResult, typing)); + if (this.log.isEnabled()) this.log.writeLine(renderPackageNameValidationFailure(validationResult, typing)); return undefined; } if (!this.typesRegistry.has(typingKey)) { if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: Entry for package '${typingKey}' does not exist in local types registry - skipping...`); return undefined; } - if (this.packageNameToTypingLocation.get(typingKey) && JsTyping.isTypingUpToDate(this.packageNameToTypingLocation.get(typingKey)!, this.typesRegistry.get(typingKey)!)) { + if (this.packageNameToTypingLocation.get(typingKey) && isTypingUpToDate(this.packageNameToTypingLocation.get(typingKey)!, this.typesRegistry.get(typingKey)!)) { if (this.log.isEnabled()) this.log.writeLine(`'${typing}':: '${typingKey}' already has an up-to-date typing - skipping...`); return undefined; } @@ -374,7 +396,7 @@ export abstract class TypingsInstaller { // packageName is guaranteed to exist in typesRegistry by filterTypings const distTags = this.typesRegistry.get(packageName)!; const newVersion = new Version(distTags[`ts${versionMajorMinor}`] || distTags[this.latestDistTag]); - const newTyping: JsTyping.CachedTyping = { typingLocation: typingFile, version: newVersion }; + const newTyping: CachedTyping = { typingLocation: typingFile, version: newVersion }; this.packageNameToTypingLocation.set(packageName, newTyping); installedTypingFiles.push(typingFile); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ee5fed651ed51..af190c705c54a 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3281,14 +3281,14 @@ declare namespace ts { isKnownTypesPackageName(name: string): boolean; installPackage(options: InstallPackageOptions): Promise; private get typingsCache(); - getCompilationSettings(): ts.CompilerOptions; - getCompilerOptions(): ts.CompilerOptions; + getCompilationSettings(): CompilerOptions; + getCompilerOptions(): CompilerOptions; getNewLine(): string; getProjectVersion(): string; getProjectReferences(): readonly ProjectReference[] | undefined; getScriptFileNames(): string[]; private getOrCreateScriptInfoAndAttachToProject; - getScriptKind(fileName: string): ts.ScriptKind; + getScriptKind(fileName: string): ScriptKind; getScriptVersion(filename: string): string; getScriptSnapshot(filename: string): IScriptSnapshot | undefined; getCancellationToken(): HostCancellationToken; @@ -3325,7 +3325,7 @@ declare namespace ts { getProjectName(): string; protected removeLocalTypingsFromTypeAcquisition(newTypeAcquisition: TypeAcquisition): TypeAcquisition; getExternalFiles(): SortedReadonlyArray; - getSourceFile(path: Path): ts.SourceFile | undefined; + getSourceFile(path: Path): SourceFile | undefined; close(): void; private detachScriptInfoIfNotRoot; isClosed(): boolean; @@ -3362,7 +3362,7 @@ declare namespace ts { filesToString(writeProjectFileNames: boolean): string; setCompilerOptions(compilerOptions: CompilerOptions): void; setTypeAcquisition(newTypeAcquisition: TypeAcquisition | undefined): void; - getTypeAcquisition(): ts.TypeAcquisition; + getTypeAcquisition(): TypeAcquisition; protected removeRoot(info: ScriptInfo): void; protected enableGlobalPlugins(options: CompilerOptions): void; protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]): void; @@ -3395,7 +3395,7 @@ declare namespace ts { getScriptFileNames(): string[]; getLanguageService(): never; getModuleResolutionHostForAutoImportProvider(): never; - getProjectReferences(): readonly ts.ProjectReference[] | undefined; + getProjectReferences(): readonly ProjectReference[] | undefined; getTypeAcquisition(): TypeAcquisition; } /** @@ -4078,6 +4078,10 @@ declare namespace ts { interface SortedArray extends Array { " __sortedArrayBrand": any; } + function isWhiteSpaceLike(ch: number): boolean; + /** Does not include line breaks. For that, see isWhiteSpaceLike. */ + function isWhiteSpaceSingleLine(ch: number): boolean; + function isLineBreak(ch: number): boolean; type Path = string & { __pathBrand: any; }; @@ -8398,6 +8402,10 @@ declare namespace ts { negative: boolean; base10Value: string; } + type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex"; + interface FileWatcher { + close(): void; + } enum FileWatcherEventKind { Created = 0, Changed = 1, @@ -8405,7 +8413,6 @@ declare namespace ts { } type FileWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, modifiedTime?: Date) => void; type DirectoryWatcherCallback = (fileName: string) => void; - type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex"; interface System { args: string[]; newLine: string; @@ -8448,17 +8455,10 @@ declare namespace ts { base64decode?(input: string): string; base64encode?(input: string): string; } - interface FileWatcher { - close(): void; - } let sys: System; function tokenToString(t: SyntaxKind): string | undefined; function getPositionOfLineAndCharacter(sourceFile: SourceFileLike, line: number, character: number): number; function getLineAndCharacterOfPosition(sourceFile: SourceFileLike, position: number): LineAndCharacter; - function isWhiteSpaceLike(ch: number): boolean; - /** Does not include line breaks. For that, see isWhiteSpaceLike. */ - function isWhiteSpaceSingleLine(ch: number): boolean; - function isLineBreak(ch: number): boolean; function couldStartTrivia(text: string, pos: number): boolean; function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean) => U): U | undefined; function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state: T): U | undefined; @@ -8818,6 +8818,7 @@ declare namespace ts { parent: ConstructorDeclaration; name: Identifier; }; + function isExternalModule(file: SourceFile): boolean; /** * This function checks multiple locations for JSDoc comments that apply to a host node. * At each location, the whole comment may apply to the node, or only a specific tag in @@ -8854,7 +8855,6 @@ declare namespace ts { * Create an external source map source file reference */ function createSourceMapSource(fileName: string, text: string, skipTrivia?: (pos: number) => number): SourceMapSource; - function setOriginalNode(node: T, original: Node | undefined): T; const factory: NodeFactory; /** * Clears any `EmitNode` entries from parse-tree nodes. @@ -9146,6 +9146,7 @@ declare namespace ts { function setTextRange(range: T, location: TextRange | undefined): T; function canHaveModifiers(node: Node): node is HasModifiers; function canHaveDecorators(node: Node): node is HasDecorators; + function setOriginalNode(node: T, original: Node | undefined): T; /** * Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes * stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise, @@ -9168,7 +9169,6 @@ declare namespace ts { * @param sourceText */ function parseJsonText(fileName: string, sourceText: string): JsonSourceFile; - function isExternalModule(file: SourceFile): boolean; function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile; interface CreateSourceFileOptions { languageVersion: ScriptTarget; @@ -9495,7 +9495,7 @@ declare namespace ts { */ function getModeForUsageLocation(file: { impliedNodeFormat?: ResolutionMode; - }, usage: StringLiteralLike): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + }, usage: StringLiteralLike): ResolutionMode; function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): readonly Diagnostic[]; /** * A function for determining if a given file is esm or cjs format, assuming modern node module resolution rules, as configured by the @@ -10528,6 +10528,10 @@ declare namespace ts { contextSpan?: TextSpan; kind: HighlightSpanKind; } + interface DocumentHighlights { + fileName: string; + highlightSpans: HighlightSpan[]; + } interface NavigateToItem { name: string; kind: ScriptElementKind; @@ -11101,10 +11105,6 @@ declare namespace ts { } /** The classifier is used for syntactic highlighting in editors via the TSServer */ function createClassifier(): Classifier; - interface DocumentHighlights { - fileName: string; - highlightSpans: HighlightSpan[]; - } function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory?: string): DocumentRegistry; /** * The document registry represents a store of SourceFile objects that can be shared between diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index debe70be3206c..e8c2c227da3ed 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -31,6 +31,10 @@ declare namespace ts { interface SortedArray extends Array { " __sortedArrayBrand": any; } + function isWhiteSpaceLike(ch: number): boolean; + /** Does not include line breaks. For that, see isWhiteSpaceLike. */ + function isWhiteSpaceSingleLine(ch: number): boolean; + function isLineBreak(ch: number): boolean; type Path = string & { __pathBrand: any; }; @@ -4351,6 +4355,10 @@ declare namespace ts { negative: boolean; base10Value: string; } + type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex"; + interface FileWatcher { + close(): void; + } enum FileWatcherEventKind { Created = 0, Changed = 1, @@ -4358,7 +4366,6 @@ declare namespace ts { } type FileWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, modifiedTime?: Date) => void; type DirectoryWatcherCallback = (fileName: string) => void; - type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex"; interface System { args: string[]; newLine: string; @@ -4401,17 +4408,10 @@ declare namespace ts { base64decode?(input: string): string; base64encode?(input: string): string; } - interface FileWatcher { - close(): void; - } let sys: System; function tokenToString(t: SyntaxKind): string | undefined; function getPositionOfLineAndCharacter(sourceFile: SourceFileLike, line: number, character: number): number; function getLineAndCharacterOfPosition(sourceFile: SourceFileLike, position: number): LineAndCharacter; - function isWhiteSpaceLike(ch: number): boolean; - /** Does not include line breaks. For that, see isWhiteSpaceLike. */ - function isWhiteSpaceSingleLine(ch: number): boolean; - function isLineBreak(ch: number): boolean; function couldStartTrivia(text: string, pos: number): boolean; function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean) => U): U | undefined; function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state: T): U | undefined; @@ -4771,6 +4771,7 @@ declare namespace ts { parent: ConstructorDeclaration; name: Identifier; }; + function isExternalModule(file: SourceFile): boolean; /** * This function checks multiple locations for JSDoc comments that apply to a host node. * At each location, the whole comment may apply to the node, or only a specific tag in @@ -4807,7 +4808,6 @@ declare namespace ts { * Create an external source map source file reference */ function createSourceMapSource(fileName: string, text: string, skipTrivia?: (pos: number) => number): SourceMapSource; - function setOriginalNode(node: T, original: Node | undefined): T; const factory: NodeFactory; /** * Clears any `EmitNode` entries from parse-tree nodes. @@ -5099,6 +5099,7 @@ declare namespace ts { function setTextRange(range: T, location: TextRange | undefined): T; function canHaveModifiers(node: Node): node is HasModifiers; function canHaveDecorators(node: Node): node is HasDecorators; + function setOriginalNode(node: T, original: Node | undefined): T; /** * Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes * stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise, @@ -5121,7 +5122,6 @@ declare namespace ts { * @param sourceText */ function parseJsonText(fileName: string, sourceText: string): JsonSourceFile; - function isExternalModule(file: SourceFile): boolean; function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile; interface CreateSourceFileOptions { languageVersion: ScriptTarget; @@ -5448,7 +5448,7 @@ declare namespace ts { */ function getModeForUsageLocation(file: { impliedNodeFormat?: ResolutionMode; - }, usage: StringLiteralLike): ModuleKind.CommonJS | ModuleKind.ESNext | undefined; + }, usage: StringLiteralLike): ResolutionMode; function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): readonly Diagnostic[]; /** * A function for determining if a given file is esm or cjs format, assuming modern node module resolution rules, as configured by the @@ -6565,6 +6565,10 @@ declare namespace ts { contextSpan?: TextSpan; kind: HighlightSpanKind; } + interface DocumentHighlights { + fileName: string; + highlightSpans: HighlightSpan[]; + } interface NavigateToItem { name: string; kind: ScriptElementKind; @@ -7138,10 +7142,6 @@ declare namespace ts { } /** The classifier is used for syntactic highlighting in editors via the TSServer */ function createClassifier(): Classifier; - interface DocumentHighlights { - fileName: string; - highlightSpans: HighlightSpan[]; - } function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory?: string): DocumentRegistry; /** * The document registry represents a store of SourceFile objects that can be shared between