mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Make first test pass
This commit is contained in:
3 files changed
+65
-14
No files matched your search
@@ -1,3 +1,5 @@
|
||||
/// <reference path="../stringCompletions.ts" />
|
||||
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
export const importFixId = "fixMissingImport";
|
||||
@@ -380,7 +382,7 @@ namespace ts.codefix {
|
||||
// "default" is a keyword and not a legal identifier for the import, so we don't expect it here
|
||||
Debug.assert(symbolName !== InternalSymbolName.Default);
|
||||
|
||||
const fixes = arrayFrom(flatMapIterator(getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, checker, program).entries(), ([_, exportInfos]) =>
|
||||
const fixes = arrayFrom(flatMapIterator(getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, checker, program, host).entries(), ([_, exportInfos]) =>
|
||||
getFixForImport(exportInfos, symbolName, symbolToken.getStart(sourceFile), program, sourceFile, host, preferences)));
|
||||
return { fixes, symbolName };
|
||||
}
|
||||
@@ -393,6 +395,7 @@ namespace ts.codefix {
|
||||
sourceFile: SourceFile,
|
||||
checker: TypeChecker,
|
||||
program: Program,
|
||||
host: LanguageServiceHost
|
||||
): ReadonlyMap<ReadonlyArray<SymbolExportInfo>> {
|
||||
// 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).
|
||||
@@ -400,7 +403,7 @@ namespace ts.codefix {
|
||||
function addSymbol(moduleSymbol: Symbol, exportedSymbol: Symbol, importKind: ImportKind): void {
|
||||
originalSymbolToExportInfos.add(getUniqueSymbolId(exportedSymbol, checker).toString(), { moduleSymbol, importKind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exportedSymbol, checker) });
|
||||
}
|
||||
forEachExternalModuleToImportFrom(checker, sourceFile, program.getSourceFiles(), moduleSymbol => {
|
||||
forEachExternalModuleToImportFrom(checker, host, sourceFile, program.getSourceFiles(), moduleSymbol => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, program.getCompilerOptions());
|
||||
@@ -564,12 +567,59 @@ namespace ts.codefix {
|
||||
return some(declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning));
|
||||
}
|
||||
|
||||
export function forEachExternalModuleToImportFrom(checker: TypeChecker, from: SourceFile, allSourceFiles: ReadonlyArray<SourceFile>, cb: (module: Symbol) => void) {
|
||||
export function forEachExternalModuleToImportFrom(checker: TypeChecker, host: LanguageServiceHost, from: SourceFile, allSourceFiles: ReadonlyArray<SourceFile>, cb: (module: Symbol) => void) {
|
||||
const packageJsons = findPackageJsons(getDirectoryPath(from.fileName), host);
|
||||
const dependencyIterator = readPackageJsonDependencies();
|
||||
forEachExternalModule(checker, allSourceFiles, (module, sourceFile) => {
|
||||
if (sourceFile === undefined || sourceFile !== from && isImportablePath(from.fileName, sourceFile.fileName)) {
|
||||
if (sourceFile === undefined || sourceFile !== from && isImportablePath(from.fileName, sourceFile.fileName) && packageJsonAllowsImporting(sourceFile.fileName)) {
|
||||
cb(module);
|
||||
}
|
||||
});
|
||||
|
||||
let seenDeps: Map<true> | undefined;
|
||||
function packageJsonAllowsImporting(fileName: string): Ternary {
|
||||
const pathComponents = getPathComponents(getDirectoryPath(fileName));
|
||||
const nodeModulesIndex = pathComponents.lastIndexOf("node_modules");
|
||||
if (nodeModulesIndex === -1) {
|
||||
return Ternary.Maybe;
|
||||
}
|
||||
const moduleSpecifier = pathComponents[nodeModulesIndex + 1] === "@types"
|
||||
? pathComponents[nodeModulesIndex + 2]
|
||||
: pathComponents[nodeModulesIndex + 1];
|
||||
if (!moduleSpecifier) {
|
||||
return Ternary.Maybe;
|
||||
}
|
||||
|
||||
if (seenDeps && seenDeps.has(moduleSpecifier)) {
|
||||
return Ternary.True;
|
||||
}
|
||||
|
||||
let packageName: string;
|
||||
while (packageName = dependencyIterator.next().value) {
|
||||
(seenDeps || (seenDeps = createMap())).set(packageName, true);
|
||||
if (packageName === moduleSpecifier) {
|
||||
return Ternary.True;
|
||||
}
|
||||
}
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
type PackageJson = Record<string, Record<string, string> | undefined>;
|
||||
function *readPackageJsonDependencies() {
|
||||
const dependencyKeys = ["dependencies", "devDependencies", "optionalDependencies"] as const;
|
||||
for (const fileName of packageJsons) {
|
||||
const content = readJson(fileName, { readFile: host.readFile ? host.readFile.bind(host) : sys.readFile }) as PackageJson;
|
||||
for (const key of dependencyKeys) {
|
||||
const dependencyHash = content[key];
|
||||
if (!dependencyHash) {
|
||||
continue;
|
||||
}
|
||||
for (const packageName in dependencyHash) {
|
||||
yield packageName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function forEachExternalModule(checker: TypeChecker, allSourceFiles: ReadonlyArray<SourceFile>, cb: (module: Symbol, sourceFile: SourceFile | undefined) => void) {
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace ts.Completions {
|
||||
return getLabelCompletionAtPosition(contextToken.parent);
|
||||
}
|
||||
|
||||
const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, preferences, /*detailsEntryId*/ undefined);
|
||||
const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, preferences, /*detailsEntryId*/ undefined, host);
|
||||
if (!completionData) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -407,10 +407,10 @@ namespace ts.Completions {
|
||||
previousToken: Node | undefined;
|
||||
readonly isJsxInitializer: IsJsxInitializer;
|
||||
}
|
||||
function getSymbolCompletionFromEntryId(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier,
|
||||
function getSymbolCompletionFromEntryId(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier, host: LanguageServiceHost
|
||||
): SymbolCompletion | { type: "request", request: Request } | { type: "literal", literal: string | number | PseudoBigInt } | { type: "none" } {
|
||||
const compilerOptions = program.getCompilerOptions();
|
||||
const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId);
|
||||
const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId, host);
|
||||
if (!completionData) {
|
||||
return { type: "none" };
|
||||
}
|
||||
@@ -472,7 +472,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
// Compute all the completion symbols again.
|
||||
const symbolCompletion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId);
|
||||
const symbolCompletion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host);
|
||||
switch (symbolCompletion.type) {
|
||||
case "request": {
|
||||
const { request } = symbolCompletion;
|
||||
@@ -557,8 +557,8 @@ namespace ts.Completions {
|
||||
return { sourceDisplay: [textPart(moduleSpecifier)], codeActions: [codeAction] };
|
||||
}
|
||||
|
||||
export function getCompletionEntrySymbol(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier): Symbol | undefined {
|
||||
const completion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId);
|
||||
export function getCompletionEntrySymbol(program: Program, log: Log, sourceFile: SourceFile, position: number, entryId: CompletionEntryIdentifier, host: LanguageServiceHost): Symbol | undefined {
|
||||
const completion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host);
|
||||
return completion.type === "symbol" ? completion.symbol : undefined;
|
||||
}
|
||||
|
||||
@@ -657,6 +657,7 @@ namespace ts.Completions {
|
||||
position: number,
|
||||
preferences: Pick<UserPreferences, "includeCompletionsForModuleExports" | "includeCompletionsWithInsertText">,
|
||||
detailsEntryId: CompletionEntryIdentifier | undefined,
|
||||
host: LanguageServiceHost
|
||||
): CompletionData | Request | undefined {
|
||||
const typeChecker = program.getTypeChecker();
|
||||
|
||||
@@ -1151,7 +1152,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
if (shouldOfferImportCompletions()) {
|
||||
getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "", program.getCompilerOptions().target!);
|
||||
getSymbolsFromOtherSourceFileExports(symbols, previousToken && isIdentifier(previousToken) ? previousToken.text : "", program.getCompilerOptions().target!, host);
|
||||
}
|
||||
filterGlobalCompletion(symbols);
|
||||
}
|
||||
@@ -1269,12 +1270,12 @@ namespace ts.Completions {
|
||||
typeChecker.getExportsOfModule(sym).some(e => symbolCanBeReferencedAtTypeLocation(e, seenModules));
|
||||
}
|
||||
|
||||
function getSymbolsFromOtherSourceFileExports(symbols: Symbol[], tokenText: string, target: ScriptTarget): void {
|
||||
function getSymbolsFromOtherSourceFileExports(symbols: Symbol[], tokenText: string, target: ScriptTarget, host: LanguageServiceHost): void {
|
||||
const tokenTextLowerCase = tokenText.toLowerCase();
|
||||
|
||||
const seenResolvedModules = createMap<true>();
|
||||
|
||||
codefix.forEachExternalModuleToImportFrom(typeChecker, sourceFile, program.getSourceFiles(), moduleSymbol => {
|
||||
codefix.forEachExternalModuleToImportFrom(typeChecker, host, sourceFile, program.getSourceFiles(), moduleSymbol => {
|
||||
// Perf -- ignore other modules if this is a request for details
|
||||
if (detailsEntryId && detailsEntryId.source && stripQuotes(moduleSymbol.name) !== detailsEntryId.source) {
|
||||
return;
|
||||
|
||||
@@ -1453,7 +1453,7 @@ namespace ts {
|
||||
|
||||
function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string): Symbol | undefined {
|
||||
synchronizeHostData();
|
||||
return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source });
|
||||
return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host);
|
||||
}
|
||||
|
||||
function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined {
|
||||
|
||||
Reference in new issue
Block a user