Eagerly resolve module specifiers for auto-import completions in --moduleResolution node12+ (#48752)

* Add failing test

* Block auto-import module specifiers including node_modules path

* Eagerly resolve module specifiers in completions in nodenext so failures can be filtered

* Add completion info flags for telemetry

* Update API baseline

* Update completions baselines

* Fix missed boolean flip

* Fix remaining tests
This commit is contained in:
Andrew Branch 2022-04-27 16:07:15 -07:00 committed by GitHub
parent 717a1be3c9
commit 476fc625df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 239 additions and 73 deletions

View File

@ -8470,7 +8470,7 @@ namespace ts {
export interface ResolvedModuleSpecifierInfo {
modulePaths: readonly ModulePath[] | undefined;
moduleSpecifiers: readonly string[] | undefined;
isAutoImportable: boolean | undefined;
isBlockedByPackageJsonDependencies: boolean | undefined;
}
/* @internal */
@ -8482,7 +8482,7 @@ namespace ts {
export interface ModuleSpecifierCache {
get(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions): Readonly<ResolvedModuleSpecifierInfo> | undefined;
set(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, modulePaths: readonly ModulePath[], moduleSpecifiers: readonly string[]): void;
setIsAutoImportable(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, isAutoImportable: boolean): void;
setBlockedByPackageJsonDependencies(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, isBlockedByPackageJsonDependencies: boolean): void;
setModulePaths(fromFileName: Path, toFileName: Path, preferences: UserPreferences, options: ModuleSpecifierOptions, modulePaths: readonly ModulePath[]): void;
clear(): void;
count(): number;

View File

@ -14,7 +14,7 @@ namespace ts.server {
return cache.get(toFileName);
},
set(fromFileName, toFileName, preferences, options, modulePaths, moduleSpecifiers) {
ensureCache(fromFileName, preferences, options).set(toFileName, createInfo(modulePaths, moduleSpecifiers, /*isAutoImportable*/ true));
ensureCache(fromFileName, preferences, options).set(toFileName, createInfo(modulePaths, moduleSpecifiers, /*isBlockedByPackageJsonDependencies*/ false));
// If any module specifiers were generated based off paths in node_modules,
// a package.json file in that package was read and is an input to the cached.
@ -43,17 +43,17 @@ namespace ts.server {
info.modulePaths = modulePaths;
}
else {
cache.set(toFileName, createInfo(modulePaths, /*moduleSpecifiers*/ undefined, /*isAutoImportable*/ undefined));
cache.set(toFileName, createInfo(modulePaths, /*moduleSpecifiers*/ undefined, /*isBlockedByPackageJsonDependencies*/ undefined));
}
},
setIsAutoImportable(fromFileName, toFileName, preferences, options, isAutoImportable) {
setBlockedByPackageJsonDependencies(fromFileName, toFileName, preferences, options, isBlockedByPackageJsonDependencies) {
const cache = ensureCache(fromFileName, preferences, options);
const info = cache.get(toFileName);
if (info) {
info.isAutoImportable = isAutoImportable;
info.isBlockedByPackageJsonDependencies = isBlockedByPackageJsonDependencies;
}
else {
cache.set(toFileName, createInfo(/*modulePaths*/ undefined, /*moduleSpecifiers*/ undefined, isAutoImportable));
cache.set(toFileName, createInfo(/*modulePaths*/ undefined, /*moduleSpecifiers*/ undefined, isBlockedByPackageJsonDependencies));
}
},
clear() {
@ -87,9 +87,9 @@ namespace ts.server {
function createInfo(
modulePaths: readonly ModulePath[] | undefined,
moduleSpecifiers: readonly string[] | undefined,
isAutoImportable: boolean | undefined,
isBlockedByPackageJsonDependencies: boolean | undefined,
): ResolvedModuleSpecifierInfo {
return { modulePaths, moduleSpecifiers, isAutoImportable };
return { modulePaths, moduleSpecifiers, isBlockedByPackageJsonDependencies };
}
}
}

View File

@ -2403,6 +2403,7 @@ namespace ts.server.protocol {
}
export interface CompletionInfo {
readonly flags?: number;
readonly isGlobalCompletion: boolean;
readonly isMemberCompletion: boolean;
readonly isNewIdentifierLocation: boolean;

View File

@ -644,6 +644,7 @@ namespace ts.codefix {
const compilerOptions = program.getCompilerOptions();
const moduleSpecifierResolutionHost = createModuleSpecifierResolutionHost(program, host);
const getChecker = memoizeOne((isFromPackageJson: boolean) => isFromPackageJson ? host.getPackageJsonAutoImportProvider!()!.getTypeChecker() : program.getTypeChecker());
const rejectNodeModulesRelativePaths = moduleResolutionUsesNodeModules(getEmitModuleResolutionKind(compilerOptions));
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);
@ -655,19 +656,19 @@ namespace ts.codefix {
const importedSymbolHasValueMeaning = !!(exportInfo.targetFlags & SymbolFlags.Value);
const addAsTypeOnly = getAddAsTypeOnly(isValidTypeOnlyUseSite, /*isForNewImportDeclaration*/ true, exportInfo.symbol, exportInfo.targetFlags, checker, compilerOptions);
computedWithoutCacheCount += computedWithoutCache ? 1 : 0;
return moduleSpecifiers?.map((moduleSpecifier): FixAddNewImport | FixAddJsdocTypeImport =>
return mapDefined(moduleSpecifiers, (moduleSpecifier): FixAddNewImport | FixAddJsdocTypeImport | undefined =>
rejectNodeModulesRelativePaths && pathContainsNodeModules(moduleSpecifier) ? undefined :
// `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types.
!importedSymbolHasValueMeaning && isJs && position !== undefined
? { kind: ImportFixKind.JsdocTypeImport, moduleSpecifier, position, exportInfo, isReExport: i > 0 }
: {
kind: ImportFixKind.AddNew,
moduleSpecifier,
importKind: getImportKind(sourceFile, exportInfo.exportKind, compilerOptions),
useRequire,
addAsTypeOnly,
exportInfo,
isReExport: i > 0,
}
!importedSymbolHasValueMeaning && isJs && position !== undefined ? { kind: ImportFixKind.JsdocTypeImport, moduleSpecifier, position, exportInfo, isReExport: i > 0 } :
{
kind: ImportFixKind.AddNew,
moduleSpecifier,
importKind: getImportKind(sourceFile, exportInfo.exportKind, compilerOptions),
useRequire,
addAsTypeOnly,
exportInfo,
isReExport: i > 0,
}
);
});

View File

@ -170,14 +170,16 @@ namespace ts.Completions {
const enum GlobalsSearch { Continue, Success, Fail }
interface ModuleSpecifierResolutioContext {
tryResolve: (exportInfo: readonly SymbolExportInfo[], symbolName: string, isFromAmbientModule: boolean) => ModuleSpecifierResolutionResult | undefined;
resolutionLimitExceeded: () => boolean;
tryResolve: (exportInfo: readonly SymbolExportInfo[], symbolName: string, isFromAmbientModule: boolean) => ModuleSpecifierResolutionResult;
resolvedAny: () => boolean;
skippedAny: () => boolean;
resolvedBeyondLimit: () => boolean;
}
interface ModuleSpecifierResolutionResult {
type ModuleSpecifierResolutionResult = "skipped" | "failed" | {
exportInfo?: SymbolExportInfo;
moduleSpecifier: string;
}
};
function resolvingModuleSpecifiers<TReturn>(
logPrefix: string,
@ -192,45 +194,56 @@ namespace ts.Completions {
): TReturn {
const start = timestamp();
const packageJsonImportFilter = createPackageJsonImportFilter(sourceFile, preferences, host);
let resolutionLimitExceeded = false;
// Under `--moduleResolution nodenext`, we have to resolve module specifiers up front, because
// package.json exports can mean we *can't* resolve a module specifier (that doesn't include a
// relative path into node_modules), and we want to filter those completions out entirely.
// Import statement completions always need specifier resolution because the module specifier is
// part of their `insertText`, not the `codeActions` creating edits away from the cursor.
const needsFullResolution = isForImportStatementCompletion || moduleResolutionRespectsExports(getEmitModuleResolutionKind(program.getCompilerOptions()));
let skippedAny = false;
let ambientCount = 0;
let resolvedCount = 0;
let resolvedFromCacheCount = 0;
let cacheAttemptCount = 0;
const result = cb({ tryResolve, resolutionLimitExceeded: () => resolutionLimitExceeded });
const result = cb({
tryResolve,
skippedAny: () => skippedAny,
resolvedAny: () => resolvedCount > 0,
resolvedBeyondLimit: () => resolvedCount > moduleSpecifierResolutionLimit,
});
const hitRateMessage = cacheAttemptCount ? ` (${(resolvedFromCacheCount / cacheAttemptCount * 100).toFixed(1)}% hit rate)` : "";
host.log?.(`${logPrefix}: resolved ${resolvedCount} module specifiers, plus ${ambientCount} ambient and ${resolvedFromCacheCount} from cache${hitRateMessage}`);
host.log?.(`${logPrefix}: response is ${resolutionLimitExceeded ? "incomplete" : "complete"}`);
host.log?.(`${logPrefix}: response is ${skippedAny ? "incomplete" : "complete"}`);
host.log?.(`${logPrefix}: ${timestamp() - start}`);
return result;
function tryResolve(exportInfo: readonly SymbolExportInfo[], symbolName: string, isFromAmbientModule: boolean): ModuleSpecifierResolutionResult | undefined {
function tryResolve(exportInfo: readonly SymbolExportInfo[], symbolName: string, isFromAmbientModule: boolean): ModuleSpecifierResolutionResult {
if (isFromAmbientModule) {
const result = codefix.getModuleSpecifierForBestExportInfo(exportInfo, symbolName, position, isValidTypeOnlyUseSite, sourceFile, program, host, preferences);
if (result) {
ambientCount++;
}
return result;
return result || "failed";
}
const shouldResolveModuleSpecifier = isForImportStatementCompletion || preferences.allowIncompleteCompletions && resolvedCount < moduleSpecifierResolutionLimit;
const shouldResolveModuleSpecifier = needsFullResolution || preferences.allowIncompleteCompletions && resolvedCount < moduleSpecifierResolutionLimit;
const shouldGetModuleSpecifierFromCache = !shouldResolveModuleSpecifier && preferences.allowIncompleteCompletions && cacheAttemptCount < moduleSpecifierResolutionCacheAttemptLimit;
const result = (shouldResolveModuleSpecifier || shouldGetModuleSpecifierFromCache)
? codefix.getModuleSpecifierForBestExportInfo(exportInfo, symbolName, position, isValidTypeOnlyUseSite, sourceFile, program, host, preferences, packageJsonImportFilter, shouldGetModuleSpecifierFromCache)
: undefined;
if (!shouldResolveModuleSpecifier && !shouldGetModuleSpecifierFromCache || shouldGetModuleSpecifierFromCache && !result) {
resolutionLimitExceeded = true;
skippedAny = true;
}
resolvedCount += result?.computedWithoutCacheCount || 0;
resolvedFromCacheCount += exportInfo.length - resolvedCount;
resolvedFromCacheCount += exportInfo.length - (result?.computedWithoutCacheCount || 0);
if (shouldGetModuleSpecifierFromCache) {
cacheAttemptCount++;
}
return result;
return result || (needsFullResolution ? "failed" : "skipped");
}
}
@ -379,7 +392,11 @@ namespace ts.Completions {
const info = exportMap.get(file.path, entry.data.exportMapKey);
const result = info && context.tryResolve(info, entry.name, !isExternalModuleNameRelative(stripQuotes(origin.moduleSymbol.name)));
if (!result) return entry;
if (result === "skipped") return entry;
if (!result || result === "failed") {
host.log?.(`Unexpected failure resolving auto import for '${entry.name}' from '${entry.source}'`);
return undefined;
}
const newOrigin: SymbolOriginInfoResolvedExport = {
...origin,
@ -394,7 +411,7 @@ namespace ts.Completions {
return entry;
});
if (!context.resolutionLimitExceeded()) {
if (!context.skippedAny()) {
previousResponse.isIncomplete = undefined;
}
@ -403,6 +420,7 @@ namespace ts.Completions {
);
previousResponse.entries = newEntries;
previousResponse.flags = (previousResponse.flags || 0) | CompletionInfoFlags.IsContinuation;
return previousResponse;
}
@ -574,12 +592,13 @@ namespace ts.Completions {
}
return {
flags: completionData.flags,
isGlobalCompletion: isInSnippetScope,
isIncomplete: preferences.allowIncompleteCompletions && hasUnresolvedAutoImports ? true : undefined,
isMemberCompletion: isMemberCompletionKind(completionKind),
isNewIdentifierLocation,
optionalReplacementSpan: getOptionalReplacementSpan(location),
entries
entries,
};
}
@ -1841,6 +1860,7 @@ namespace ts.Completions {
readonly isRightOfOpenTag: boolean;
readonly importCompletionNode?: Node;
readonly hasUnresolvedAutoImports?: boolean;
readonly flags: CompletionInfoFlags;
}
type Request =
| { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag }
@ -2025,6 +2045,7 @@ namespace ts.Completions {
let location = getTouchingPropertyName(sourceFile, position);
let keywordFilters = KeywordCompletionFilters.None;
let isNewIdentifierLocation = false;
let flags = CompletionInfoFlags.None;
if (contextToken) {
const importStatementCompletion = getImportStatementCompletionInfo(contextToken);
@ -2045,6 +2066,7 @@ namespace ts.Completions {
// is not backward compatible with older clients, the language service defaults to disabling it, allowing newer clients
// to opt in with the `includeCompletionsForImportStatements` user preference.
importCompletionNode = importStatementCompletion.replacementNode;
flags |= CompletionInfoFlags.IsImportStatementCompletion;
}
// Bail out if this is a known invalid completion location
if (!importCompletionNode && isCompletionListBlocker(contextToken)) {
@ -2252,6 +2274,7 @@ namespace ts.Completions {
isRightOfOpenTag,
importCompletionNode,
hasUnresolvedAutoImports,
flags,
};
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag | JSDocTemplateTag;
@ -2692,6 +2715,7 @@ namespace ts.Completions {
return;
}
flags |= CompletionInfoFlags.MayIncludeAutoImports;
// import { type | -> token text should be blank
const isAfterTypeOnlyImportSpecifierModifier = previousToken === contextToken
&& importCompletionNode
@ -2736,14 +2760,34 @@ namespace ts.Completions {
return;
}
const defaultExportInfo = find(info, isImportableExportInfo);
if (!defaultExportInfo) {
// Do a relatively cheap check to bail early if all re-exports are non-importable
// due to file location or package.json dependency filtering. For non-node12+
// module resolution modes, getting past this point guarantees that we'll be
// able to generate a suitable module specifier, so we can safely show a completion,
// even if we defer computing the module specifier.
const firstImportableExportInfo = find(info, isImportableExportInfo);
if (!firstImportableExportInfo) {
return;
}
// If we don't need to resolve module specifiers, we can use any re-export that is importable at all
// (We need to ensure that at least one is importable to show a completion.)
const { exportInfo = defaultExportInfo, moduleSpecifier } = context.tryResolve(info, symbolName, isFromAmbientModule) || {};
// In node12+, module specifier resolution can fail due to modules being blocked
// by package.json `exports`. If that happens, don't show a completion item.
// N.B. in this resolution mode we always try to resolve module specifiers here,
// because we have to know now if it's going to fail so we can omit the completion
// from the list.
const result = context.tryResolve(info, symbolName, isFromAmbientModule) || {};
if (result === "failed") return;
// If we skipped resolving module specifiers, our selection of which ExportInfo
// to use here is arbitrary, since the info shown in the completion list derived from
// it should be identical regardless of which one is used. During the subsequent
// `CompletionEntryDetails` request, we'll get all the ExportInfos again and pick
// the best one based on the module specifier it produces.
let exportInfo = firstImportableExportInfo, moduleSpecifier;
if (result !== "skipped") {
({ exportInfo = firstImportableExportInfo, moduleSpecifier } = result);
}
const isDefaultExport = exportInfo.exportKind === ExportKind.Default;
const symbol = isDefaultExport && getLocalSymbolForExportDefault(exportInfo.symbol) || exportInfo.symbol;
@ -2761,7 +2805,9 @@ namespace ts.Completions {
}
);
hasUnresolvedAutoImports = context.resolutionLimitExceeded();
hasUnresolvedAutoImports = context.skippedAny();
flags |= context.resolvedAny() ? CompletionInfoFlags.ResolvedModuleSpecifiers : 0;
flags |= context.resolvedBeyondLimit() ? CompletionInfoFlags.ResolvedModuleSpecifiersBeyondLimit : 0;
}
);
@ -2831,6 +2877,7 @@ namespace ts.Completions {
return;
}
const origin: SymbolOriginInfoObjectLiteralMethod = { kind: SymbolOriginInfoKind.ObjectLiteralMethod, ...entryProps };
flags |= CompletionInfoFlags.MayIncludeMethodSnippets;
symbolToOriginInfoMap[symbols.length] = origin;
symbols.push(member);
});

View File

@ -291,8 +291,8 @@ namespace ts {
): boolean {
if (from === to) return false;
const cachedResult = moduleSpecifierCache?.get(from.path, to.path, preferences, {});
if (cachedResult?.isAutoImportable !== undefined) {
return cachedResult.isAutoImportable;
if (cachedResult?.isBlockedByPackageJsonDependencies !== undefined) {
return !cachedResult.isBlockedByPackageJsonDependencies;
}
const getCanonicalFileName = hostGetCanonicalFileName(moduleSpecifierResolutionHost);
@ -313,7 +313,7 @@ namespace ts {
if (packageJsonFilter) {
const isAutoImportable = hasImportablePath && packageJsonFilter.allowsImportingSourceFile(to, moduleSpecifierResolutionHost);
moduleSpecifierCache?.setIsAutoImportable(from.path, to.path, preferences, {}, isAutoImportable);
moduleSpecifierCache?.setBlockedByPackageJsonDependencies(from.path, to.path, preferences, {}, !isAutoImportable);
return isAutoImportable;
}

View File

@ -1176,7 +1176,20 @@ namespace ts {
argumentCount: number;
}
// Do not change existing values, as they exist in telemetry.
export const enum CompletionInfoFlags {
None = 0,
MayIncludeAutoImports = 1 << 0,
IsImportStatementCompletion = 1 << 1,
IsContinuation = 1 << 2,
ResolvedModuleSpecifiers = 1 << 3,
ResolvedModuleSpecifiersBeyondLimit = 1 << 4,
MayIncludeMethodSnippets = 1 << 5,
}
export interface CompletionInfo {
/** For performance telemetry. */
flags?: CompletionInfoFlags;
/** Not true for all global completions. This will be true if the enclosing scope matches a few syntax kinds. See `isSnippetScope`. */
isGlobalCompletion: boolean;
isMemberCompletion: boolean;

View File

@ -1928,6 +1928,14 @@ namespace ts {
};
}
export function moduleResolutionRespectsExports(moduleResolution: ModuleResolutionKind): boolean {
return moduleResolution >= ModuleResolutionKind.Node12 && moduleResolution <= ModuleResolutionKind.NodeNext;
}
export function moduleResolutionUsesNodeModules(moduleResolution: ModuleResolutionKind): boolean {
return moduleResolution === ModuleResolutionKind.NodeJs || moduleResolution >= ModuleResolutionKind.Node12 && moduleResolution <= ModuleResolutionKind.NodeNext;
}
export function makeImportIfNecessary(defaultImport: Identifier | undefined, namedImports: readonly ImportSpecifier[] | undefined, moduleSpecifier: string, quotePreference: QuotePreference): ImportDeclaration | undefined {
return defaultImport || namedImports && namedImports.length ? makeImport(defaultImport, namedImports, moduleSpecifier, quotePreference) : undefined;
}

View File

@ -52,6 +52,7 @@ namespace ts.projectSystem {
assert.isString(exportMapKey);
delete (response?.entries[0].data as any).exportMapKey;
assert.deepEqual<protocol.CompletionInfo | undefined>(response, {
flags: CompletionInfoFlags.MayIncludeAutoImports,
isGlobalCompletion: true,
isIncomplete: undefined,
isMemberCompletion: false,

View File

@ -77,6 +77,7 @@ namespace ts.projectSystem {
command: protocol.CommandTypes.CompletionInfo,
arguments: completionRequestArgs
}, {
flags: 0,
isGlobalCompletion: false,
isMemberCompletion: true,
isNewIdentifierLocation: false,

View File

@ -39,7 +39,7 @@ namespace ts.projectSystem {
describe("unittests:: tsserver:: moduleSpecifierCache", () => {
it("caches importability within a file", () => {
const { moduleSpecifierCache } = setup();
assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, {}, {})?.isAutoImportable);
assert.isFalse(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, {}, {})?.isBlockedByPackageJsonDependencies);
});
it("caches module specifiers within a file", () => {
@ -54,7 +54,7 @@ namespace ts.projectSystem {
isRedirect: false
}],
moduleSpecifiers: ["mobx"],
isAutoImportable: true,
isBlockedByPackageJsonDependencies: false,
});
});
@ -72,7 +72,7 @@ namespace ts.projectSystem {
const { host, moduleSpecifierCache } = setup();
host.writeFile("/src/a2.ts", aTs.content);
host.runQueuedTimeoutCallbacks();
assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, {}, {})?.isAutoImportable);
assert.isFalse(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, {}, {})?.isBlockedByPackageJsonDependencies);
});
it("invalidates the cache when symlinks are added or removed", () => {

View File

@ -6435,7 +6435,18 @@ declare namespace ts {
argumentIndex: number;
argumentCount: number;
}
enum CompletionInfoFlags {
None = 0,
MayIncludeAutoImports = 1,
IsImportStatementCompletion = 2,
IsContinuation = 4,
ResolvedModuleSpecifiers = 8,
ResolvedModuleSpecifiersBeyondLimit = 16,
MayIncludeMethodSnippets = 32
}
interface CompletionInfo {
/** For performance telemetry. */
flags?: CompletionInfoFlags;
/** Not true for all global completions. This will be true if the enclosing scope matches a few syntax kinds. See `isSnippetScope`. */
isGlobalCompletion: boolean;
isMemberCompletion: boolean;
@ -8823,6 +8834,7 @@ declare namespace ts.server.protocol {
body?: CompletionInfo;
}
interface CompletionInfo {
readonly flags?: number;
readonly isGlobalCompletion: boolean;
readonly isMemberCompletion: boolean;
readonly isNewIdentifierLocation: boolean;

View File

@ -6435,7 +6435,18 @@ declare namespace ts {
argumentIndex: number;
argumentCount: number;
}
enum CompletionInfoFlags {
None = 0,
MayIncludeAutoImports = 1,
IsImportStatementCompletion = 2,
IsContinuation = 4,
ResolvedModuleSpecifiers = 8,
ResolvedModuleSpecifiersBeyondLimit = 16,
MayIncludeMethodSnippets = 32
}
interface CompletionInfo {
/** For performance telemetry. */
flags?: CompletionInfoFlags;
/** Not true for all global completions. This will be true if the enclosing scope matches a few syntax kinds. See `isSnippetScope`. */
isGlobalCompletion: boolean;
isMemberCompletion: boolean;

View File

@ -6,6 +6,7 @@
"name": ""
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,

View File

@ -6,6 +6,7 @@
"name": "0"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -73,6 +74,7 @@
"name": "1"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,

View File

@ -6,6 +6,7 @@
"name": "26"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,

View File

@ -6,6 +6,7 @@
"name": "4"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -754,6 +755,7 @@
"name": "5"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -4818,6 +4820,7 @@
"name": "7"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -5566,6 +5569,7 @@
"name": "9"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -6314,6 +6318,7 @@
"name": "11"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -7062,6 +7067,7 @@
"name": "12"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -7810,6 +7816,7 @@
"name": "13"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,
@ -11874,6 +11881,7 @@
"name": "16"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -12622,6 +12630,7 @@
"name": "17"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -16686,6 +16695,7 @@
"name": "19"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -17434,6 +17444,7 @@
"name": "21"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -18182,6 +18193,7 @@
"name": "23"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -18930,6 +18942,7 @@
"name": "24"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -19678,6 +19691,7 @@
"name": "25"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,
@ -23742,6 +23756,7 @@
"name": "29"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -27806,6 +27821,7 @@
"name": "30"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -28961,6 +28977,7 @@
"name": "31"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -33025,6 +33042,7 @@
"name": "33"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -37043,6 +37061,7 @@
"name": "34"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -38198,6 +38217,7 @@
"name": "35"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,
@ -42216,6 +42236,7 @@
"name": "36"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -43371,6 +43392,7 @@
"name": "38"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -47435,6 +47457,7 @@
"name": "39"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -48590,6 +48613,7 @@
"name": "40"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,
@ -52654,6 +52678,7 @@
"name": "41"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -53809,6 +53834,7 @@
"name": "42"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,
@ -57873,6 +57899,7 @@
"name": "45"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -61932,6 +61959,7 @@
"name": "49"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,
@ -65991,6 +66019,7 @@
"name": "52"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -70050,6 +70079,7 @@
"name": "56"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,
@ -74109,6 +74139,7 @@
"name": "59"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -78168,6 +78199,7 @@
"name": "63"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,
@ -82227,6 +82259,7 @@
"name": "67"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -82610,6 +82643,7 @@
"name": "87"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,
@ -86782,6 +86816,7 @@
"name": "88"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -87937,6 +87972,7 @@
"name": "109"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -92138,6 +92174,7 @@
"name": "110"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -92362,6 +92399,7 @@
"name": "114"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,
@ -92461,6 +92499,7 @@
"name": "115"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,

View File

@ -6,6 +6,7 @@
"name": "18"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -5653,6 +5654,7 @@
"name": "15"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -12096,6 +12098,7 @@
"name": "24"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -17719,6 +17722,7 @@
"name": "27"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -23419,6 +23423,7 @@
"name": "39"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -29161,6 +29166,7 @@
"name": "44"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -35604,6 +35610,7 @@
"name": ""
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,

View File

@ -6,6 +6,7 @@
"name": "3"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -4316,6 +4317,7 @@
"name": "7"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": true,
@ -7792,6 +7794,7 @@
"name": "9"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,

View File

@ -6,6 +6,7 @@
"name": "2"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -3853,6 +3854,7 @@
"name": "4"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -8575,6 +8577,7 @@
"name": "15"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,
@ -12241,6 +12244,7 @@
"name": "17"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": true,
"isMemberCompletion": false,
"isNewIdentifierLocation": false,

View File

@ -6,6 +6,7 @@
"name": "14"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,

View File

@ -6,6 +6,7 @@
"name": "2"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,

View File

@ -6,6 +6,7 @@
"name": "1"
},
"completionList": {
"flags": 0,
"isGlobalCompletion": false,
"isMemberCompletion": true,
"isNewIdentifierLocation": false,

View File

@ -0,0 +1,35 @@
/// <reference path="fourslash.ts" />
// @module: nodenext
// @Filename: /node_modules/pack/package.json
//// {
//// "name": "pack",
//// "version": "1.0.0",
//// "exports": {
//// ".": "./main.mjs"
//// }
//// }
// @Filename: /node_modules/pack/main.d.mts
//// import {} from "./unreachable.mjs";
//// export const fromMain = 0;
// @Filename: /node_modules/pack/unreachable.d.mts
//// export const fromUnreachable = 0;
// @Filename: /index.mts
//// import { fromMain } from "pack";
//// fromUnreachable/**/
goTo.marker("");
verify.importFixAtPosition([]);
verify.completions({
marker: "",
excludes: ["fromUnreachable"],
preferences: {
includeCompletionsForModuleExports: true,
allowIncompleteCompletions: false,
}
});

View File

@ -39,16 +39,6 @@ goTo.marker("");
verify.completions({
marker: "",
exact: completion.globalsPlus([{
// TODO: We should filter this one out due to its bad module specifier,
// but we don't know it's going to be filtered out until we actually
// resolve the module specifier, which is a problem for completions
// that don't have their module specifiers eagerly resolved.
name: "fooFromIndex",
source: "../node_modules/dependency/lib/index.js",
sourceDisplay: "../node_modules/dependency/lib/index.js",
sortText: completion.SortText.AutoImportSuggestions,
hasAction: true,
}, {
name: "fooFromLol",
source: "dependency",
sourceDisplay: "dependency",

View File

@ -49,13 +49,6 @@ goTo.marker("cts");
verify.completions({
marker: "cts",
exact: completion.globalsPlus([{
// TODO: this one will go away (see note in ./autoImportProvider_exportMap3.ts)
name: "fooFromIndex",
source: "../node_modules/dependency/lib/index",
sourceDisplay: "../node_modules/dependency/lib/index",
sortText: completion.SortText.AutoImportSuggestions,
hasAction: true,
}, {
name: "fooFromLol",
source: "dependency/lol",
sourceDisplay: "dependency/lol",
@ -78,13 +71,6 @@ verify.completions({
sourceDisplay: "dependency/lol",
sortText: completion.SortText.AutoImportSuggestions,
hasAction: true,
}, {
// TODO: this one will go away (see note in ./autoImportProvider_exportMap3.ts)
name: "fooFromLol",
source: "../node_modules/dependency/lib/lol.js",
sourceDisplay: "../node_modules/dependency/lib/lol.js",
sortText: completion.SortText.AutoImportSuggestions,
hasAction: true,
}]),
preferences: {
includeCompletionsForModuleExports: true,