mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-18 13:59:04 -05:00
Allow ImportAdder to insert imports into a new file
This commit is contained in:
committed by
navya9singh
parent
68d8be4f1d
commit
fab885d440
@@ -31,6 +31,7 @@ import {
|
||||
flatten,
|
||||
forEach,
|
||||
forEachAncestorDirectory,
|
||||
FutureSourceFile,
|
||||
getBaseFileName,
|
||||
GetCanonicalFileName,
|
||||
getConditions,
|
||||
@@ -126,7 +127,7 @@ interface Preferences {
|
||||
function getPreferences(
|
||||
{ importModuleSpecifierPreference, importModuleSpecifierEnding }: UserPreferences,
|
||||
compilerOptions: CompilerOptions,
|
||||
importingSourceFile: SourceFile,
|
||||
importingSourceFile: SourceFile | FutureSourceFile,
|
||||
oldImportSpecifier?: string,
|
||||
): Preferences {
|
||||
const preferredEnding = getPreferredEnding();
|
||||
@@ -212,7 +213,7 @@ export function getModuleSpecifier(
|
||||
/** @internal */
|
||||
export function getNodeModulesPackageName(
|
||||
compilerOptions: CompilerOptions,
|
||||
importingSourceFile: SourceFile,
|
||||
importingSourceFile: SourceFile | FutureSourceFile,
|
||||
nodeModulesFileName: string,
|
||||
host: ModuleSpecifierResolutionHost,
|
||||
preferences: UserPreferences,
|
||||
@@ -243,14 +244,14 @@ function getModuleSpecifierWorker(
|
||||
/** @internal */
|
||||
export function tryGetModuleSpecifiersFromCache(
|
||||
moduleSymbol: Symbol,
|
||||
importingSourceFile: SourceFile,
|
||||
importingSourceFilePath: Path,
|
||||
host: ModuleSpecifierResolutionHost,
|
||||
userPreferences: UserPreferences,
|
||||
options: ModuleSpecifierOptions = {},
|
||||
): readonly string[] | undefined {
|
||||
return tryGetModuleSpecifiersFromCacheWorker(
|
||||
moduleSymbol,
|
||||
importingSourceFile,
|
||||
importingSourceFilePath,
|
||||
host,
|
||||
userPreferences,
|
||||
options)[0];
|
||||
@@ -258,7 +259,7 @@ export function tryGetModuleSpecifiersFromCache(
|
||||
|
||||
function tryGetModuleSpecifiersFromCacheWorker(
|
||||
moduleSymbol: Symbol,
|
||||
importingSourceFile: SourceFile,
|
||||
importingSourceFilePath: Path,
|
||||
host: ModuleSpecifierResolutionHost,
|
||||
userPreferences: UserPreferences,
|
||||
options: ModuleSpecifierOptions = {},
|
||||
@@ -269,7 +270,7 @@ function tryGetModuleSpecifiersFromCacheWorker(
|
||||
}
|
||||
|
||||
const cache = host.getModuleSpecifierCache?.();
|
||||
const cached = cache?.get(importingSourceFile.path, moduleSourceFile.path, userPreferences, options);
|
||||
const cached = cache?.get(importingSourceFilePath, moduleSourceFile.path, userPreferences, options);
|
||||
return [cached?.moduleSpecifiers, moduleSourceFile, cached?.modulePaths, cache];
|
||||
}
|
||||
|
||||
@@ -303,7 +304,7 @@ export function getModuleSpecifiersWithCacheInfo(
|
||||
moduleSymbol: Symbol,
|
||||
checker: TypeChecker,
|
||||
compilerOptions: CompilerOptions,
|
||||
importingSourceFile: SourceFile,
|
||||
importingSourceFile: SourceFile | FutureSourceFile,
|
||||
host: ModuleSpecifierResolutionHost,
|
||||
userPreferences: UserPreferences,
|
||||
options: ModuleSpecifierOptions = {},
|
||||
@@ -315,7 +316,7 @@ export function getModuleSpecifiersWithCacheInfo(
|
||||
// eslint-disable-next-line prefer-const
|
||||
let [specifiers, moduleSourceFile, modulePaths, cache] = tryGetModuleSpecifiersFromCacheWorker(
|
||||
moduleSymbol,
|
||||
importingSourceFile,
|
||||
importingSourceFile.path,
|
||||
host,
|
||||
userPreferences,
|
||||
options
|
||||
@@ -333,14 +334,14 @@ export function getModuleSpecifiersWithCacheInfo(
|
||||
function computeModuleSpecifiers(
|
||||
modulePaths: readonly ModulePath[],
|
||||
compilerOptions: CompilerOptions,
|
||||
importingSourceFile: SourceFile,
|
||||
importingSourceFile: SourceFile | FutureSourceFile,
|
||||
host: ModuleSpecifierResolutionHost,
|
||||
userPreferences: UserPreferences,
|
||||
options: ModuleSpecifierOptions = {},
|
||||
): readonly string[] {
|
||||
const info = getInfo(importingSourceFile.path, host);
|
||||
const preferences = getPreferences(userPreferences, compilerOptions, importingSourceFile);
|
||||
const existingSpecifier = forEach(modulePaths, modulePath => forEach(
|
||||
const existingSpecifier = importingSourceFile.kind && forEach(modulePaths, modulePath => forEach(
|
||||
host.getFileIncludeReasons().get(toPath(modulePath.path, host.getCurrentDirectory(), info.getCanonicalFileName)),
|
||||
reason => {
|
||||
if (reason.kind !== FileIncludeKind.Import || reason.file !== importingSourceFile.path) return undefined;
|
||||
@@ -877,7 +878,7 @@ function tryGetModuleNameFromRootDirs(rootDirs: readonly string[], moduleFileNam
|
||||
return processEnding(shortest, allowedEndings, compilerOptions);
|
||||
}
|
||||
|
||||
function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCanonicalFileName, sourceDirectory }: Info, importingSourceFile: SourceFile, host: ModuleSpecifierResolutionHost, options: CompilerOptions, userPreferences: UserPreferences, packageNameOnly?: boolean, overrideMode?: ResolutionMode): string | undefined {
|
||||
function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCanonicalFileName, sourceDirectory }: Info, importingSourceFile: SourceFile | FutureSourceFile, host: ModuleSpecifierResolutionHost, options: CompilerOptions, userPreferences: UserPreferences, packageNameOnly?: boolean, overrideMode?: ResolutionMode): string | undefined {
|
||||
if (!host.fileExists || !host.readFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -4220,6 +4220,15 @@ export interface SourceFileLike {
|
||||
getPositionOfLineAndCharacter?(line: number, character: number, allowEdits?: true): number;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface FutureSourceFile {
|
||||
readonly kind?: undefined;
|
||||
readonly fileName: string;
|
||||
readonly path: Path;
|
||||
readonly impliedNodeFormat?: ResolutionMode;
|
||||
readonly commonJsModuleIndicator?: boolean;
|
||||
readonly externalModuleIndicator?: boolean;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface RedirectInfo {
|
||||
|
||||
@@ -157,6 +157,7 @@ import {
|
||||
FunctionDeclaration,
|
||||
FunctionExpression,
|
||||
FunctionLikeDeclaration,
|
||||
FutureSourceFile,
|
||||
GetAccessorDeclaration,
|
||||
getBaseFileName,
|
||||
GetCanonicalFileName,
|
||||
@@ -9160,7 +9161,7 @@ export function usesExtensionsOnImports({ imports }: SourceFile, hasExtension: (
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getModuleSpecifierEndingPreference(preference: UserPreferences["importModuleSpecifierEnding"], resolutionMode: ResolutionMode, compilerOptions: CompilerOptions, sourceFile: SourceFile): ModuleSpecifierEnding {
|
||||
export function getModuleSpecifierEndingPreference(preference: UserPreferences["importModuleSpecifierEnding"], resolutionMode: ResolutionMode, compilerOptions: CompilerOptions, sourceFile: SourceFile | FutureSourceFile): ModuleSpecifierEnding {
|
||||
if (preference === "js" || resolutionMode === ModuleKind.ESNext) {
|
||||
// Extensions are explicitly requested or required. Now choose between .js and .ts.
|
||||
if (!shouldAllowImportingTsExtension(compilerOptions)) {
|
||||
@@ -9185,27 +9186,30 @@ export function getModuleSpecifierEndingPreference(preference: UserPreferences["
|
||||
// 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 sourceFile.kind && 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 (hasTSFileExtension(specifier)) {
|
||||
return ModuleSpecifierEnding.TsExtension;
|
||||
}
|
||||
if (hasJSFileExtension(specifier)) {
|
||||
usesJsExtensions = true;
|
||||
if (sourceFile.kind) {
|
||||
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 (hasTSFileExtension(specifier)) {
|
||||
return ModuleSpecifierEnding.TsExtension;
|
||||
}
|
||||
if (hasJSFileExtension(specifier)) {
|
||||
usesJsExtensions = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return usesJsExtensions ? ModuleSpecifierEnding.JsExtension : ModuleSpecifierEnding.Minimal;
|
||||
}
|
||||
return usesJsExtensions ? ModuleSpecifierEnding.JsExtension : ModuleSpecifierEnding.Minimal;
|
||||
return ModuleSpecifierEnding.Minimal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
FunctionExpression,
|
||||
getEmitScriptTarget,
|
||||
getNameOfDeclaration,
|
||||
getQuotePreference,
|
||||
getQuotePreferenceFromFile,
|
||||
getTokenAtPosition,
|
||||
idText,
|
||||
isAccessExpression,
|
||||
@@ -211,7 +211,7 @@ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, po
|
||||
|
||||
// f.x = expr
|
||||
if (isAccessExpression(memberDeclaration) && (isFunctionExpression(assignmentExpr) || isArrowFunction(assignmentExpr))) {
|
||||
const quotePreference = getQuotePreference(sourceFile, preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(sourceFile, preferences);
|
||||
const name = tryGetPropertyName(memberDeclaration, compilerOptions, quotePreference);
|
||||
if (name) {
|
||||
createFunctionLikeExpressionMember(members, assignmentExpr, name);
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
FunctionExpression,
|
||||
getEmitScriptTarget,
|
||||
getModeForUsageLocation,
|
||||
getQuotePreference,
|
||||
getQuotePreferenceFromFile,
|
||||
getResolvedModule,
|
||||
getSynthesizedDeepClone,
|
||||
getSynthesizedDeepClones,
|
||||
@@ -87,10 +87,10 @@ registerCodeFix({
|
||||
getCodeActions(context) {
|
||||
const { sourceFile, program, preferences } = context;
|
||||
const changes = textChanges.ChangeTracker.with(context, changes => {
|
||||
const moduleExportsChangedToDefault = convertFileToEsModule(sourceFile, program.getTypeChecker(), changes, getEmitScriptTarget(program.getCompilerOptions()), getQuotePreference(sourceFile, preferences));
|
||||
const moduleExportsChangedToDefault = convertFileToEsModule(sourceFile, program.getTypeChecker(), changes, getEmitScriptTarget(program.getCompilerOptions()), getQuotePreferenceFromFile(sourceFile, preferences));
|
||||
if (moduleExportsChangedToDefault) {
|
||||
for (const importingFile of program.getSourceFiles()) {
|
||||
fixImportOfModuleExports(importingFile, sourceFile, changes, getQuotePreference(importingFile, preferences));
|
||||
fixImportOfModuleExports(importingFile, sourceFile, changes, getQuotePreferenceFromFile(importingFile, preferences));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
getNodeId,
|
||||
getObjectFlags,
|
||||
getOrUpdate,
|
||||
getQuotePreference,
|
||||
getQuotePreferenceFromFile,
|
||||
getSourceFileOfNode,
|
||||
getTokenAtPosition,
|
||||
hasAbstractModifier,
|
||||
@@ -597,7 +597,7 @@ function addEnumMemberDeclaration(changes: textChanges.ChangeTracker, checker: T
|
||||
}
|
||||
|
||||
function addFunctionDeclaration(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: FunctionInfo | SignatureInfo) {
|
||||
const quotePreference = getQuotePreference(context.sourceFile, context.preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(context.sourceFile, context.preferences);
|
||||
const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host);
|
||||
const functionDeclaration = info.kind === InfoKind.Function
|
||||
? createSignatureDeclarationFromCallExpression(SyntaxKind.FunctionDeclaration, context, importAdder, info.call, idText(info.token), info.modifierFlags, info.parentDeclaration)
|
||||
@@ -614,7 +614,7 @@ function addFunctionDeclaration(changes: textChanges.ChangeTracker, context: Cod
|
||||
|
||||
function addJsxAttributes(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: JsxAttributesInfo) {
|
||||
const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host);
|
||||
const quotePreference = getQuotePreference(context.sourceFile, context.preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(context.sourceFile, context.preferences);
|
||||
const checker = context.program.getTypeChecker();
|
||||
const jsxAttributesNode = info.parentDeclaration.attributes;
|
||||
const hasSpreadAttribute = some(jsxAttributesNode.properties, isJsxSpreadAttribute);
|
||||
@@ -634,7 +634,7 @@ function addJsxAttributes(changes: textChanges.ChangeTracker, context: CodeFixCo
|
||||
|
||||
function addObjectLiteralProperties(changes: textChanges.ChangeTracker, context: CodeFixContextBase, info: ObjectLiteralInfo) {
|
||||
const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host);
|
||||
const quotePreference = getQuotePreference(context.sourceFile, context.preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(context.sourceFile, context.preferences);
|
||||
const target = getEmitScriptTarget(context.program.getCompilerOptions());
|
||||
const checker = context.program.getTypeChecker();
|
||||
const props = map(info.properties, prop => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
findAncestor,
|
||||
getEmitModuleKind,
|
||||
getNamespaceDeclarationNode,
|
||||
getQuotePreference,
|
||||
getQuotePreferenceFromFile,
|
||||
getSourceFileOfNode,
|
||||
getTokenAtPosition,
|
||||
ImportDeclaration,
|
||||
@@ -39,7 +39,7 @@ function getCodeFixesForImportDeclaration(context: CodeFixContext, node: ImportD
|
||||
const variations: CodeFixAction[] = [];
|
||||
|
||||
// import Bluebird from "bluebird";
|
||||
variations.push(createAction(context, sourceFile, node, makeImport(namespace.name, /*namedImports*/ undefined, node.moduleSpecifier, getQuotePreference(sourceFile, context.preferences))));
|
||||
variations.push(createAction(context, sourceFile, node, makeImport(namespace.name, /*namedImports*/ undefined, node.moduleSpecifier, getQuotePreferenceFromFile(sourceFile, context.preferences))));
|
||||
|
||||
if (getEmitModuleKind(opts) === ModuleKind.CommonJS) {
|
||||
// import Bluebird = require("bluebird");
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
cast,
|
||||
Diagnostics,
|
||||
factory,
|
||||
getQuotePreference,
|
||||
getQuotePreferenceFromFile,
|
||||
getTokenAtPosition,
|
||||
isPropertyAccessChain,
|
||||
isPropertyAccessExpression,
|
||||
@@ -37,7 +37,7 @@ registerCodeFix({
|
||||
});
|
||||
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: PropertyAccessExpression, preferences: UserPreferences): void {
|
||||
const quotePreference = getQuotePreference(sourceFile, preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(sourceFile, preferences);
|
||||
const argumentsExpression = factory.createStringLiteral(node.name.text, quotePreference === QuotePreference.Single);
|
||||
changes.replaceNode(
|
||||
sourceFile,
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
getModuleSpecifierResolverHost,
|
||||
getNameForExportedSymbol,
|
||||
getNameOfDeclaration,
|
||||
getQuotePreference,
|
||||
getQuotePreferenceFromFile,
|
||||
getSetAccessorValueParameter,
|
||||
getSynthesizedDeepClone,
|
||||
getTokenAtPosition,
|
||||
@@ -205,7 +205,7 @@ export function addNewNodeForMemberSymbol(
|
||||
const type = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration));
|
||||
const optional = !!(symbol.flags & SymbolFlags.Optional);
|
||||
const ambient = !!(enclosingDeclaration.flags & NodeFlags.Ambient) || isAmbient;
|
||||
const quotePreference = getQuotePreference(sourceFile, preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(sourceFile, preferences);
|
||||
|
||||
switch (kind) {
|
||||
case SyntaxKind.PropertySignature:
|
||||
@@ -467,7 +467,7 @@ export function createSignatureDeclarationFromCallExpression(
|
||||
modifierFlags: ModifierFlags,
|
||||
contextNode: Node
|
||||
): MethodDeclaration | FunctionDeclaration | MethodSignature {
|
||||
const quotePreference = getQuotePreference(context.sourceFile, context.preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(context.sourceFile, context.preferences);
|
||||
const scriptTarget = getEmitScriptTarget(context.program.getCompilerOptions());
|
||||
const tracker = getNoopSymbolTrackerWithResolver(context);
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
flatMapIterator,
|
||||
forEachExternalModuleToImportFrom,
|
||||
formatting,
|
||||
FutureSourceFile,
|
||||
getAllowSyntheticDefaultImports,
|
||||
getBaseFileName,
|
||||
getDefaultExportInfoWorker,
|
||||
@@ -46,11 +47,13 @@ import {
|
||||
getNodeId,
|
||||
getQuoteFromPreference,
|
||||
getQuotePreference,
|
||||
getQuotePreferenceFromFile,
|
||||
getSourceFileOfNode,
|
||||
getSymbolId,
|
||||
getTokenAtPosition,
|
||||
getTypeKeywordOfTypeOnlyImport,
|
||||
getUniqueSymbolId,
|
||||
hasJSFileExtension,
|
||||
hostGetCanonicalFileName,
|
||||
Identifier,
|
||||
ImportClause,
|
||||
@@ -61,7 +64,6 @@ import {
|
||||
ImportsNotUsedAsValues,
|
||||
insertImports,
|
||||
InternalSymbolName,
|
||||
isExternalModule,
|
||||
isExternalModuleReference,
|
||||
isIdentifier,
|
||||
isIdentifierPart,
|
||||
@@ -208,7 +210,7 @@ interface AddToExistingState {
|
||||
readonly namedImports: Map<string, AddAsTypeOnly>;
|
||||
}
|
||||
|
||||
function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAutoImportProvider: boolean, preferences: UserPreferences, host: LanguageServiceHost, cancellationToken: CancellationToken | undefined): ImportAdder {
|
||||
function createImportAdderWorker(sourceFile: SourceFile | FutureSourceFile, 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[] = [];
|
||||
@@ -232,7 +234,7 @@ function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAu
|
||||
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 exportInfo = getAllExportInfoForSymbol(sourceFile.path, 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) {
|
||||
@@ -346,14 +348,17 @@ function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAu
|
||||
}
|
||||
|
||||
function writeFixes(changeTracker: textChanges.ChangeTracker) {
|
||||
const quotePreference = getQuotePreference(sourceFile, preferences);
|
||||
const quotePreference = sourceFile.kind ? getQuotePreferenceFromFile(sourceFile, preferences) : getQuotePreference(preferences);
|
||||
for (const fix of addToNamespace) {
|
||||
Debug.assert(sourceFile.kind, "Cannot add to an existing import in a non-existent file.");
|
||||
addNamespaceQualifier(changeTracker, sourceFile, fix);
|
||||
}
|
||||
for (const fix of importType) {
|
||||
Debug.assert(sourceFile.kind, "Cannot add to an existing import in a non-existent file.");
|
||||
addImportType(changeTracker, sourceFile, fix, quotePreference);
|
||||
}
|
||||
addToExisting.forEach(({ importClauseOrBindingPattern, defaultImport, namedImports }) => {
|
||||
Debug.assert(sourceFile.kind, "Cannot add to an existing import in a non-existent file.");
|
||||
doAddExistingFix(
|
||||
changeTracker,
|
||||
sourceFile,
|
||||
@@ -509,14 +514,14 @@ export function getImportCompletionAction(
|
||||
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);
|
||||
exportInfos = getExportInfoMap(sourceFile.path, 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);
|
||||
: getAllExportInfoForSymbol(sourceFile.path, targetSymbol, symbolName, moduleSymbol, isJsxTagName, program, host, preferences, cancellationToken);
|
||||
Debug.assertIsDefined(exportInfos, "Some exportInfo should match the specified symbol / moduleSymbol");
|
||||
}
|
||||
|
||||
@@ -545,7 +550,7 @@ export function getPromoteTypeOnlyCompletionAction(sourceFile: SourceFile, symbo
|
||||
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) {
|
||||
function getImportFixForSymbol(sourceFile: SourceFile | FutureSourceFile, 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);
|
||||
}
|
||||
@@ -554,10 +559,10 @@ function codeFixActionToCodeAction({ description, changes, commands }: CodeFixAc
|
||||
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 {
|
||||
function getAllExportInfoForSymbol(importingFilePath: Path, 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 => {
|
||||
return getExportInfoMap(importingFilePath, host, program, preferences, cancellationToken)
|
||||
.search(importingFilePath, 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;
|
||||
}
|
||||
@@ -591,16 +596,16 @@ function getImportFixes(
|
||||
isValidTypeOnlyUseSite: boolean,
|
||||
useRequire: boolean,
|
||||
program: Program,
|
||||
sourceFile: SourceFile,
|
||||
sourceFile: SourceFile | FutureSourceFile,
|
||||
host: LanguageServiceHost,
|
||||
preferences: UserPreferences,
|
||||
importMap = createExistingImportMap(program.getTypeChecker(), sourceFile, program.getCompilerOptions()),
|
||||
importMap = sourceFile.kind && 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());
|
||||
const existingImports = importMap && flatMap(exportInfos, importMap.getImportsForExportInfo);
|
||||
const useNamespace = existingImports && usagePosition !== undefined && tryUseExistingNamespaceImport(existingImports, usagePosition);
|
||||
const addToExisting = existingImports && 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 {
|
||||
@@ -787,9 +792,9 @@ function createExistingImportMap(checker: TypeChecker, importingFile: SourceFile
|
||||
};
|
||||
}
|
||||
|
||||
function shouldUseRequire(sourceFile: SourceFile, program: Program): boolean {
|
||||
function shouldUseRequire(sourceFile: SourceFile | FutureSourceFile, program: Program): boolean {
|
||||
// 1. TypeScript files don't use require variable declarations
|
||||
if (!isSourceFileJS(sourceFile)) {
|
||||
if (sourceFile.kind && !isSourceFileJS(sourceFile) || !sourceFile.kind && !hasJSFileExtension(sourceFile.fileName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -820,7 +825,7 @@ function createGetChecker(program: Program, host: LanguageServiceHost) {
|
||||
|
||||
function getNewImportFixes(
|
||||
program: Program,
|
||||
sourceFile: SourceFile,
|
||||
sourceFile: SourceFile | FutureSourceFile,
|
||||
usagePosition: number | undefined,
|
||||
isValidTypeOnlyUseSite: boolean,
|
||||
useRequire: boolean,
|
||||
@@ -829,14 +834,14 @@ function getNewImportFixes(
|
||||
preferences: UserPreferences,
|
||||
fromCacheOnly?: boolean,
|
||||
): { computedWithoutCacheCount: number, fixes: readonly (FixAddNewImport | FixAddJsdocTypeImport)[] } {
|
||||
const isJs = isSourceFileJS(sourceFile);
|
||||
const isJs = sourceFile.kind ? isSourceFileJS(sourceFile) : hasJSFileExtension(sourceFile.fileName);
|
||||
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) => ({ moduleSpecifiers: moduleSpecifiers.tryGetModuleSpecifiersFromCache(moduleSymbol, sourceFile.path, moduleSpecifierResolutionHost, preferences), computedWithoutCache: false })
|
||||
: (moduleSymbol: Symbol, checker: TypeChecker) => moduleSpecifiers.getModuleSpecifiersWithCacheInfo(moduleSymbol, checker, compilerOptions, sourceFile, moduleSpecifierResolutionHost, preferences);
|
||||
|
||||
let computedWithoutCacheCount = 0;
|
||||
@@ -892,9 +897,9 @@ function getNewImportFixes(
|
||||
|
||||
function getFixesForAddImport(
|
||||
exportInfos: readonly SymbolExportInfo[],
|
||||
existingImports: readonly FixAddToExistingImportInfo[],
|
||||
existingImports: readonly FixAddToExistingImportInfo[] | undefined,
|
||||
program: Program,
|
||||
sourceFile: SourceFile,
|
||||
sourceFile: SourceFile | FutureSourceFile,
|
||||
usagePosition: number | undefined,
|
||||
isValidTypeOnlyUseSite: boolean,
|
||||
useRequire: boolean,
|
||||
@@ -958,7 +963,7 @@ function sortFixInfo(fixes: readonly (FixInfo & { fix: ImportFixWithModuleSpecif
|
||||
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 {
|
||||
function getBestFix(fixes: readonly ImportFixWithModuleSpecifier[], sourceFile: SourceFile | FutureSourceFile, 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) {
|
||||
@@ -982,7 +987,7 @@ function getBestFix(fixes: readonly ImportFixWithModuleSpecifier[], sourceFile:
|
||||
function compareModuleSpecifiers(
|
||||
a: ImportFixWithModuleSpecifier,
|
||||
b: ImportFixWithModuleSpecifier,
|
||||
importingFile: SourceFile,
|
||||
importingFile: SourceFile | FutureSourceFile,
|
||||
program: Program,
|
||||
allowsImportingSpecifier: (specifier: string) => boolean,
|
||||
toPath: (fileName: string) => Path,
|
||||
@@ -1003,7 +1008,7 @@ function compareModuleSpecifiers(
|
||||
// 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 {
|
||||
function isFixPossiblyReExportingImportingFile(fix: ImportFixWithModuleSpecifier, importingFile: SourceFile | FutureSourceFile, compilerOptions: CompilerOptions, toPath: (fileName: string) => Path): boolean {
|
||||
if (fix.isReExport &&
|
||||
fix.exportInfo?.moduleFileName &&
|
||||
getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node10 &&
|
||||
@@ -1019,7 +1024,7 @@ 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 {
|
||||
function compareNodeCoreModuleSpecifiers(a: string, b: string, importingFile: SourceFile | FutureSourceFile, 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;
|
||||
@@ -1064,7 +1069,7 @@ function getUmdSymbol(token: Node, checker: TypeChecker): Symbol | undefined {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function getImportKind(importingFile: SourceFile, exportKind: ExportKind, compilerOptions: CompilerOptions, forceImportKeyword?: boolean): ImportKind {
|
||||
export function getImportKind(importingFile: SourceFile | FutureSourceFile, 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;
|
||||
@@ -1078,7 +1083,7 @@ export function getImportKind(importingFile: SourceFile, exportKind: ExportKind,
|
||||
}
|
||||
}
|
||||
|
||||
function getUmdImportKind(importingFile: SourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind {
|
||||
function getUmdImportKind(importingFile: SourceFile | FutureSourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind {
|
||||
// Import a synthetic `default` if enabled.
|
||||
if (getAllowSyntheticDefaultImports(compilerOptions)) {
|
||||
return ImportKind.Default;
|
||||
@@ -1090,8 +1095,8 @@ function getUmdImportKind(importingFile: SourceFile, compilerOptions: CompilerOp
|
||||
case ModuleKind.AMD:
|
||||
case ModuleKind.CommonJS:
|
||||
case ModuleKind.UMD:
|
||||
if (isInJSFile(importingFile)) {
|
||||
return isExternalModule(importingFile) || forceImportKeyword ? ImportKind.Namespace : ImportKind.CommonJS;
|
||||
if (importingFile.kind ? isInJSFile(importingFile) : hasJSFileExtension(importingFile.fileName)) {
|
||||
return importingFile.externalModuleIndicator || forceImportKeyword ? ImportKind.Namespace : ImportKind.CommonJS;
|
||||
}
|
||||
return ImportKind.CommonJS;
|
||||
case ModuleKind.System:
|
||||
@@ -1206,9 +1211,9 @@ function getExportInfos(
|
||||
return originalSymbolToExportInfos;
|
||||
}
|
||||
|
||||
function getExportEqualsImportKind(importingFile: SourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind {
|
||||
function getExportEqualsImportKind(importingFile: SourceFile | FutureSourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind {
|
||||
const allowSyntheticDefaults = getAllowSyntheticDefaultImports(compilerOptions);
|
||||
const isJS = isInJSFile(importingFile);
|
||||
const isJS = importingFile.kind ? isInJSFile(importingFile) : hasJSFileExtension(importingFile.fileName);
|
||||
// 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) {
|
||||
@@ -1217,17 +1222,19 @@ function getExportEqualsImportKind(importingFile: SourceFile, compilerOptions: C
|
||||
// 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
|
||||
return importingFile.externalModuleIndicator || 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;
|
||||
if (importingFile.kind) {
|
||||
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
|
||||
@@ -1243,7 +1250,7 @@ function codeActionForFix(context: textChanges.TextChangesContext, sourceFile: S
|
||||
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);
|
||||
const quotePreference = getQuotePreferenceFromFile(sourceFile, preferences);
|
||||
switch (fix.kind) {
|
||||
case ImportFixKind.UseNamespace:
|
||||
addNamespaceQualifier(changes, sourceFile, fix);
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
AnyImportSyntax,
|
||||
Diagnostics,
|
||||
Expression,
|
||||
getQuotePreference,
|
||||
getQuotePreferenceFromFile,
|
||||
getTokenAtPosition,
|
||||
Identifier,
|
||||
isExternalModuleReference,
|
||||
@@ -57,5 +57,5 @@ function getInfo(sourceFile: SourceFile, pos: number): Info | undefined {
|
||||
}
|
||||
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, info: Info, preferences: UserPreferences): void {
|
||||
changes.replaceNode(sourceFile, info.importNode, makeImport(info.name, /*namedImports*/ undefined, info.moduleSpecifier, getQuotePreference(sourceFile, preferences)));
|
||||
changes.replaceNode(sourceFile, info.importNode, makeImport(info.name, /*namedImports*/ undefined, info.moduleSpecifier, getQuotePreferenceFromFile(sourceFile, preferences)));
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ import {
|
||||
getNewLineKind,
|
||||
getNewLineOrDefaultFromHost,
|
||||
getPropertyNameForPropertyNameNode,
|
||||
getQuotePreference,
|
||||
getQuotePreferenceFromFile,
|
||||
getReplacementSpanForContextToken,
|
||||
getRootDeclaration,
|
||||
getSourceFileOfModule,
|
||||
@@ -790,7 +790,7 @@ function continuePreviousIncompleteResponse(
|
||||
|
||||
const touchNode = getTouchingPropertyName(file, position);
|
||||
const lowerCaseTokenText = location.text.toLowerCase();
|
||||
const exportMap = getExportInfoMap(file, host, program, preferences, cancellationToken);
|
||||
const exportMap = getExportInfoMap(file.path, host, program, preferences, cancellationToken);
|
||||
const newEntries = resolvingModuleSpecifiers(
|
||||
"continuePreviousIncompleteResponse",
|
||||
host,
|
||||
@@ -1105,7 +1105,7 @@ function getJSDocParamAnnotation(
|
||||
const inferredType = checker.getTypeAtLocation(initializer.parent);
|
||||
if (!(inferredType.flags & (TypeFlags.Any | TypeFlags.Void))) {
|
||||
const sourceFile = initializer.getSourceFile();
|
||||
const quotePreference = getQuotePreference(sourceFile, preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(sourceFile, preferences);
|
||||
const builderFlags = (quotePreference === QuotePreference.Single ? NodeBuilderFlags.UseSingleQuotesForStringLiteralType : NodeBuilderFlags.None);
|
||||
const typeNode = checker.typeToTypeNode(inferredType, findAncestor(initializer, isFunctionLike), builderFlags);
|
||||
if (typeNode) {
|
||||
@@ -1353,7 +1353,7 @@ function getExhaustiveCaseSnippets(
|
||||
const tracker = newCaseClauseTracker(checker, clauses);
|
||||
|
||||
const target = getEmitScriptTarget(options);
|
||||
const quotePreference = getQuotePreference(sourceFile, preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(sourceFile, preferences);
|
||||
const importAdder = codefix.createImportAdder(sourceFile, program, preferences, host);
|
||||
const elements: Expression[] = [];
|
||||
for (const type of switchType.types as LiteralType[]) {
|
||||
@@ -2048,7 +2048,7 @@ function createObjectLiteralMethod(
|
||||
const declaration = declarations[0];
|
||||
const name = getSynthesizedDeepClone(getNameOfDeclaration(declaration), /*includeTrivia*/ false) as PropertyName;
|
||||
const type = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration));
|
||||
const quotePreference = getQuotePreference(sourceFile, preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(sourceFile, preferences);
|
||||
const builderFlags = NodeBuilderFlags.OmitThisParameter | (quotePreference === QuotePreference.Single ? NodeBuilderFlags.UseSingleQuotesForStringLiteralType : NodeBuilderFlags.None);
|
||||
|
||||
switch (declaration.kind) {
|
||||
@@ -3747,7 +3747,7 @@ function getCompletionData(
|
||||
"";
|
||||
|
||||
const moduleSpecifierCache = host.getModuleSpecifierCache?.();
|
||||
const exportInfo = getExportInfoMap(sourceFile, host, program, preferences, cancellationToken);
|
||||
const exportInfo = getExportInfoMap(sourceFile.path, host, program, preferences, cancellationToken);
|
||||
const packageJsonAutoImportProvider = host.getPackageJsonAutoImportProvider?.();
|
||||
const packageJsonFilter = detailsEntryId ? undefined : createPackageJsonImportFilter(sourceFile, preferences, host);
|
||||
resolvingModuleSpecifiers(
|
||||
|
||||
@@ -451,7 +451,7 @@ function forEachExternalModule(checker: TypeChecker, allSourceFiles: readonly So
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getExportInfoMap(importingFile: SourceFile, host: LanguageServiceHost, program: Program, preferences: UserPreferences, cancellationToken: CancellationToken | undefined): ExportInfoMap {
|
||||
export function getExportInfoMap(importingFilePath: Path, host: LanguageServiceHost, program: Program, preferences: UserPreferences, cancellationToken: CancellationToken | undefined): ExportInfoMap {
|
||||
const start = timestamp();
|
||||
// Pulling the AutoImportProvider project will trigger its updateGraph if pending,
|
||||
// which will invalidate the export map cache if things change, so pull it before
|
||||
@@ -463,7 +463,7 @@ export function getExportInfoMap(importingFile: SourceFile, host: LanguageServic
|
||||
getGlobalTypingsCacheLocation: () => host.getGlobalTypingsCacheLocation?.(),
|
||||
});
|
||||
|
||||
if (cache.isUsableByFile(importingFile.path)) {
|
||||
if (cache.isUsableByFile(importingFilePath)) {
|
||||
host.log?.("getExportInfoMap: cache hit");
|
||||
return cache;
|
||||
}
|
||||
@@ -481,7 +481,7 @@ export function getExportInfoMap(importingFile: SourceFile, host: LanguageServic
|
||||
// can cause it to happen: see 'completionsImport_mergedReExport.ts'
|
||||
if (defaultInfo && isImportableSymbol(defaultInfo.symbol, checker)) {
|
||||
cache.add(
|
||||
importingFile.path,
|
||||
importingFilePath,
|
||||
defaultInfo.symbol,
|
||||
defaultInfo.exportKind === ExportKind.Default ? InternalSymbolName.Default : InternalSymbolName.ExportEquals,
|
||||
moduleSymbol,
|
||||
@@ -493,7 +493,7 @@ export function getExportInfoMap(importingFile: SourceFile, host: LanguageServic
|
||||
checker.forEachExportAndPropertyOfModule(moduleSymbol, (exported, key) => {
|
||||
if (exported !== defaultInfo?.symbol && isImportableSymbol(exported, checker) && addToSeen(seenExports, key)) {
|
||||
cache.add(
|
||||
importingFile.path,
|
||||
importingFilePath,
|
||||
exported,
|
||||
key,
|
||||
moduleSymbol,
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
getLocaleSpecificMessage,
|
||||
getModifiers,
|
||||
getPropertySymbolFromBindingElement,
|
||||
getQuotePreference,
|
||||
getQuotePreferenceFromFile,
|
||||
getRangesWhere,
|
||||
getRefactorContextSpan,
|
||||
getRelativePathFromFile,
|
||||
@@ -280,7 +280,7 @@ function getNewStatementsAndRemoveFromOldFile(
|
||||
}
|
||||
|
||||
const useEsModuleSyntax = !!oldFile.externalModuleIndicator;
|
||||
const quotePreference = getQuotePreference(oldFile, preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(oldFile, preferences);
|
||||
const importsFromNewFile = createOldFileImportsFromNewFile(oldFile, usage.oldFileImportsFromNewFile, newFilename, program, host, useEsModuleSyntax, quotePreference);
|
||||
if (importsFromNewFile) {
|
||||
insertImports(changes, oldFile, importsFromNewFile, /*blankLineBetween*/ true, preferences);
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
DeclarationStatement,
|
||||
EmitHint,
|
||||
EmitTextWriter,
|
||||
emptyArray,
|
||||
endsWith,
|
||||
Expression,
|
||||
factory,
|
||||
@@ -40,6 +41,7 @@ import {
|
||||
formatting,
|
||||
FunctionDeclaration,
|
||||
FunctionExpression,
|
||||
FutureSourceFile,
|
||||
getAncestor,
|
||||
getFirstNonSpaceCharacterPosition,
|
||||
getFormatCodeSettingsForWriting,
|
||||
@@ -337,6 +339,12 @@ interface ChangeText extends BaseChange {
|
||||
readonly text: string;
|
||||
}
|
||||
|
||||
interface NewFileInsertion {
|
||||
readonly fileName: string;
|
||||
readonly oldFile?: SourceFile;
|
||||
readonly statements: readonly (Statement | SyntaxKind.NewLineTrivia)[];
|
||||
}
|
||||
|
||||
function getAdjustedRange(sourceFile: SourceFile, startNode: Node, endNode: Node, options: ConfigurableStartEnd): TextRange {
|
||||
return { pos: getAdjustedStartPosition(sourceFile, startNode, options), end: getAdjustedEndPosition(sourceFile, endNode, options) };
|
||||
}
|
||||
@@ -480,7 +488,7 @@ export function isThisTypeAnnotatable(containingFunction: SignatureDeclaration):
|
||||
/** @internal */
|
||||
export class ChangeTracker {
|
||||
private readonly changes: Change[] = [];
|
||||
private readonly newFiles: { readonly oldFile: SourceFile | undefined, readonly fileName: string, readonly statements: readonly (Statement | SyntaxKind.NewLineTrivia)[] }[] = [];
|
||||
private newFileChanges?: NewFileInsertion[];
|
||||
private readonly classesWithNodesInsertedAtStart = new Map<number, { readonly node: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, readonly sourceFile: SourceFile }>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
|
||||
private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node | NodeArray<TypeParameterDeclaration> }[] = [];
|
||||
|
||||
@@ -600,28 +608,42 @@ export class ChangeTracker {
|
||||
this.replaceRangeWithNodes(sourceFile, createRange(pos), newNodes, options);
|
||||
}
|
||||
|
||||
public insertNodeAtTopOfFile(sourceFile: SourceFile, newNode: Statement, blankLineBetween: boolean): void {
|
||||
public insertNodeAtTopOfFile(sourceFile: SourceFile | FutureSourceFile, newNode: Statement, blankLineBetween: boolean): void {
|
||||
this.insertAtTopOfFile(sourceFile, newNode, blankLineBetween);
|
||||
}
|
||||
|
||||
public insertNodesAtTopOfFile(sourceFile: SourceFile, newNodes: readonly Statement[], blankLineBetween: boolean): void {
|
||||
public insertNodesAtTopOfFile(sourceFile: SourceFile | FutureSourceFile, newNodes: readonly Statement[], blankLineBetween: boolean): void {
|
||||
this.insertAtTopOfFile(sourceFile, newNodes, blankLineBetween);
|
||||
}
|
||||
|
||||
private insertAtTopOfFile(sourceFile: SourceFile, insert: Statement | readonly Statement[], blankLineBetween: boolean): void {
|
||||
const pos = getInsertionPositionAtSourceFileTop(sourceFile);
|
||||
private insertAtTopOfFile(sourceFile: SourceFile | FutureSourceFile, insert: Statement | readonly Statement[], blankLineBetween: boolean): void {
|
||||
const pos = sourceFile.kind ? getInsertionPositionAtSourceFileTop(sourceFile) : 0;
|
||||
const options = {
|
||||
prefix: pos === 0 ? undefined : this.newLineCharacter,
|
||||
suffix: (isLineBreak(sourceFile.text.charCodeAt(pos)) ? "" : this.newLineCharacter) + (blankLineBetween ? this.newLineCharacter : ""),
|
||||
suffix: (sourceFile.kind && isLineBreak(sourceFile.text.charCodeAt(pos)) ? "" : this.newLineCharacter) + (blankLineBetween ? this.newLineCharacter : ""),
|
||||
};
|
||||
if (isArray(insert)) {
|
||||
this.insertNodesAt(sourceFile, pos, insert, options);
|
||||
if (sourceFile.kind) {
|
||||
this.insertNodesAt(sourceFile, pos, insert, options);
|
||||
}
|
||||
else {
|
||||
this.insertStatementsInNewFile(sourceFile.fileName, insert);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.insertNodeAt(sourceFile, pos, insert, options);
|
||||
if (sourceFile.kind) {
|
||||
this.insertNodeAt(sourceFile, pos, insert, options);
|
||||
}
|
||||
else {
|
||||
this.insertStatementsInNewFile(sourceFile.fileName, [insert]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private insertStatementsInNewFile(fileName: string, statements: readonly (Statement | SyntaxKind.NewLineTrivia)[], oldFile?: SourceFile): void {
|
||||
(this.newFileChanges ??= []).push({ fileName, statements, oldFile });
|
||||
}
|
||||
|
||||
public insertFirstParameter(sourceFile: SourceFile, parameters: NodeArray<ParameterDeclaration>, newParam: ParameterDeclaration): void {
|
||||
const p0 = firstOrUndefined(parameters);
|
||||
if (p0) {
|
||||
@@ -1128,14 +1150,14 @@ export class ChangeTracker {
|
||||
this.finishDeleteDeclarations();
|
||||
this.finishClassesWithNodesInsertedAtStart();
|
||||
const changes = changesToText.getTextChangesFromChanges(this.changes, this.newLineCharacter, this.formatContext, validate);
|
||||
for (const { oldFile, fileName, statements } of this.newFiles) {
|
||||
for (const { oldFile, fileName, statements } of this.newFileChanges ?? emptyArray) {
|
||||
changes.push(changesToText.newFileChanges(oldFile, fileName, statements, this.newLineCharacter, this.formatContext));
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
public createNewFile(oldFile: SourceFile | undefined, fileName: string, statements: readonly (Statement | SyntaxKind.NewLineTrivia)[]): void {
|
||||
this.newFiles.push({ oldFile, fileName, statements });
|
||||
this.insertStatementsInNewFile(fileName, statements, oldFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,11 +85,13 @@ import {
|
||||
FunctionDeclaration,
|
||||
FunctionExpression,
|
||||
FunctionLikeDeclaration,
|
||||
FutureSourceFile,
|
||||
getAssignmentDeclarationKind,
|
||||
getCombinedNodeFlagsAlwaysIncludeJSDoc,
|
||||
getDirectoryPath,
|
||||
getEmitScriptTarget,
|
||||
getExternalModuleImportEqualsDeclarationExpression,
|
||||
getImpliedNodeFormatForFile,
|
||||
getIndentString,
|
||||
getJSDocEnumTag,
|
||||
getLastChild,
|
||||
@@ -267,6 +269,8 @@ import {
|
||||
ModifierFlags,
|
||||
ModuleDeclaration,
|
||||
ModuleInstanceState,
|
||||
ModuleKind,
|
||||
ModuleResolutionHost,
|
||||
ModuleResolutionKind,
|
||||
ModuleSpecifierResolutionHost,
|
||||
moduleSpecifiers,
|
||||
@@ -350,6 +354,7 @@ import {
|
||||
textSpanEnd,
|
||||
Token,
|
||||
tokenToString,
|
||||
toPath,
|
||||
tryCast,
|
||||
Type,
|
||||
TypeChecker,
|
||||
@@ -2472,9 +2477,14 @@ export function quotePreferenceFromString(str: StringLiteral, sourceFile: Source
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getQuotePreference(sourceFile: SourceFile, preferences: UserPreferences): QuotePreference {
|
||||
export function getQuotePreference(preferences: UserPreferences): QuotePreference {
|
||||
return preferences.quotePreference === "single" ? QuotePreference.Single : QuotePreference.Double;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getQuotePreferenceFromFile(sourceFile: SourceFile, preferences: UserPreferences): QuotePreference {
|
||||
if (preferences.quotePreference && preferences.quotePreference !== "auto") {
|
||||
return preferences.quotePreference === "single" ? QuotePreference.Single : QuotePreference.Double;
|
||||
return getQuotePreference(preferences);
|
||||
}
|
||||
else {
|
||||
// ignore synthetic import added when importHelpers: true
|
||||
@@ -2561,17 +2571,18 @@ 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: textChanges.ChangeTracker, sourceFile: SourceFile | FutureSourceFile, 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);
|
||||
const existingImportStatements = sourceFile.kind ? filter(sourceFile.statements, importKindPredicate) : undefined;
|
||||
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];
|
||||
if (!existingImportStatements.length) {
|
||||
if (!existingImportStatements?.length) {
|
||||
changes.insertNodesAtTopOfFile(sourceFile, sortedNewImports, blankLineBetween);
|
||||
}
|
||||
else if (existingImportStatements && (sortKind = OrganizeImports.detectImportDeclarationSorting(existingImportStatements, preferences))) {
|
||||
Debug.assert(sourceFile.kind, "Cannot have existing import statements in a non-existent source file.");
|
||||
const comparer = OrganizeImports.getOrganizeImportsComparer(preferences, sortKind === SortKind.CaseInsensitive);
|
||||
for (const newImport of sortedNewImports) {
|
||||
const insertionIndex = OrganizeImports.getImportDeclarationInsertionIndex(existingImportStatements, newImport, comparer);
|
||||
@@ -2590,6 +2601,7 @@ export function insertImports(changes: textChanges.ChangeTracker, sourceFile: So
|
||||
else {
|
||||
const lastExistingImport = lastOrUndefined(existingImportStatements);
|
||||
if (lastExistingImport) {
|
||||
Debug.assert(sourceFile.kind, "Cannot have existing import statements in a non-existent source file.");
|
||||
changes.insertNodesAfter(sourceFile, lastExistingImport, sortedNewImports);
|
||||
}
|
||||
else {
|
||||
@@ -3322,7 +3334,7 @@ export function getContextualTypeFromParent(node: Expression, checker: TypeCheck
|
||||
/** @internal */
|
||||
export function quote(sourceFile: SourceFile, preferences: UserPreferences, text: string): string {
|
||||
// Editors can pass in undefined or empty string - we want to infer the preference in those cases.
|
||||
const quotePreference = getQuotePreference(sourceFile, preferences);
|
||||
const quotePreference = getQuotePreferenceFromFile(sourceFile, preferences);
|
||||
const quoted = JSON.stringify(text);
|
||||
return quotePreference === QuotePreference.Single ? `'${stripQuotes(quoted).replace(/'/g, "\\'").replace(/\\"/g, '"')}'` : quoted;
|
||||
}
|
||||
@@ -3683,7 +3695,7 @@ export interface PackageJsonImportFilter {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function createPackageJsonImportFilter(fromFile: SourceFile, preferences: UserPreferences, host: LanguageServiceHost): PackageJsonImportFilter {
|
||||
export function createPackageJsonImportFilter(fromFile: SourceFile | FutureSourceFile, preferences: UserPreferences, host: LanguageServiceHost): PackageJsonImportFilter {
|
||||
const packageJsons = (
|
||||
(host.getPackageJsonsVisibleToFile && host.getPackageJsonsVisibleToFile(fromFile.fileName)) || getPackageJsonsVisibleToFile(fromFile.fileName, host)
|
||||
).filter(p => p.parseable);
|
||||
@@ -3783,7 +3795,7 @@ export function createPackageJsonImportFilter(fromFile: SourceFile, preferences:
|
||||
// from Node core modules or not. We can start by seeing if the user is actually using
|
||||
// any node core modules, as opposed to simply having @types/node accidentally as a
|
||||
// dependency of a dependency.
|
||||
if (isSourceFileJS(fromFile) && JsTyping.nodeCoreModules.has(moduleSpecifier)) {
|
||||
if (fromFile.kind && isSourceFileJS(fromFile) && JsTyping.nodeCoreModules.has(moduleSpecifier)) {
|
||||
if (usesNodeCoreModules === undefined) {
|
||||
usesNodeCoreModules = consumesNodeCoreModules(fromFile);
|
||||
}
|
||||
@@ -4041,8 +4053,8 @@ export function isDeprecatedDeclaration(decl: Declaration) {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function shouldUseUriStyleNodeCoreModules(file: SourceFile, program: Program): boolean {
|
||||
const decisionFromFile = firstDefined(file.imports, node => {
|
||||
export function shouldUseUriStyleNodeCoreModules(file: SourceFile | FutureSourceFile, program: Program): boolean {
|
||||
const decisionFromFile = file.kind && firstDefined(file.imports, node => {
|
||||
if (JsTyping.nodeCoreModules.has(node.text)) {
|
||||
return startsWith(node.text, "node:");
|
||||
}
|
||||
@@ -4064,6 +4076,27 @@ export function diagnosticToString(diag: DiagnosticOrDiagnosticAndArguments): st
|
||||
: getLocaleSpecificMessage(diag);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function createFutureSourceFile(
|
||||
fileName: string,
|
||||
program: Program,
|
||||
host: ModuleResolutionHost,
|
||||
moduleSyntax?: ModuleKind.CommonJS | ModuleKind.ESNext,
|
||||
): FutureSourceFile {
|
||||
const path = toPath(fileName, /*basePath*/ undefined, program.getCanonicalFileName);
|
||||
const impliedNodeFormat = getImpliedNodeFormatForFile(path, program.getPackageJsonInfoCache?.(), host, program.getCompilerOptions());
|
||||
if (moduleSyntax === ModuleKind.CommonJS && impliedNodeFormat === ModuleKind.ESNext) {
|
||||
Debug.fail("ES Modules cannot contain CommonJS syntax");
|
||||
}
|
||||
return {
|
||||
fileName,
|
||||
path,
|
||||
commonJsModuleIndicator: moduleSyntax === ModuleKind.CommonJS,
|
||||
externalModuleIndicator: moduleSyntax === ModuleKind.ESNext,
|
||||
impliedNodeFormat,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get format code settings for a code writing context (e.g. when formatting text changes or completions code).
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user