Only look up package.json type if module is node16/nodenext or file is in node_modules (#58825)

Co-authored-by: Jake Bailey <5341706+jakebailey@users.noreply.github.com>
This commit is contained in:
Andrew Branch
2024-07-17 09:23:51 -07:00
committed by GitHub
co-authored by Jake Bailey
parent f37482cd16
commit a9139bfdfe
90 changed files with 3082 additions and 355 deletions
+30 -18
View File
@@ -50,10 +50,12 @@ import {
getDeclarationOfKind,
getDefaultLikeExportInfo,
getDirectoryPath,
getEmitModuleFormatOfFileWorker,
getEmitModuleKind,
getEmitModuleResolutionKind,
getEmitScriptTarget,
getExportInfoMap,
getImpliedNodeFormatForEmitWorker,
getIsFileExcluded,
getMeaningFromLocation,
getNameForExportedSymbol,
@@ -335,7 +337,7 @@ function createImportAdderWorker(sourceFile: SourceFile | FutureSourceFile, prog
compilerOptions,
createModuleSpecifierResolutionHost(program, host),
);
const importKind = getImportKind(futureExportingSourceFile, exportKind, compilerOptions);
const importKind = getImportKind(futureExportingSourceFile, exportKind, program);
const addAsTypeOnly = getAddAsTypeOnly(
isImportUsageValidAsTypeOnly,
/*isForNewImportDeclaration*/ true,
@@ -699,7 +701,7 @@ export interface ImportSpecifierResolver {
/** @internal */
export function createImportSpecifierResolver(importingFile: SourceFile, program: Program, host: LanguageServiceHost, preferences: UserPreferences): ImportSpecifierResolver {
const packageJsonImportFilter = createPackageJsonImportFilter(importingFile, preferences, host);
const importMap = createExistingImportMap(program.getTypeChecker(), importingFile, program.getCompilerOptions());
const importMap = createExistingImportMap(importingFile, program);
return { getModuleSpecifierForBestExportInfo };
function getModuleSpecifierForBestExportInfo(
@@ -911,7 +913,7 @@ function getImportFixes(
sourceFile: SourceFile | FutureSourceFile,
host: LanguageServiceHost,
preferences: UserPreferences,
importMap = isFullSourceFile(sourceFile) ? createExistingImportMap(program.getTypeChecker(), sourceFile, program.getCompilerOptions()) : undefined,
importMap = isFullSourceFile(sourceFile) ? createExistingImportMap(sourceFile, program) : undefined,
fromCacheOnly?: boolean,
): { computedWithoutCacheCount: number; fixes: readonly ImportFixWithModuleSpecifier[]; } {
const checker = program.getTypeChecker();
@@ -1078,7 +1080,8 @@ function tryAddToExistingImport(existingImports: readonly FixAddToExistingImport
}
}
function createExistingImportMap(checker: TypeChecker, importingFile: SourceFile, compilerOptions: CompilerOptions) {
function createExistingImportMap(importingFile: SourceFile, program: Program) {
const checker = program.getTypeChecker();
let importMap: MultiMap<SymbolId, AnyImportOrRequire> | undefined;
for (const moduleSpecifier of importingFile.imports) {
const i = importFromModuleSpecifier(moduleSpecifier);
@@ -1108,7 +1111,7 @@ function createExistingImportMap(checker: TypeChecker, importingFile: SourceFile
&& !every(matchingDeclarations, isJSDocImportTag)
) return emptyArray;
const importKind = getImportKind(importingFile, exportKind, compilerOptions);
const importKind = getImportKind(importingFile, exportKind, program);
return matchingDeclarations.map(declaration => ({ declaration, importKind, symbol, targetFlags }));
},
};
@@ -1132,8 +1135,9 @@ function shouldUseRequire(sourceFile: SourceFile | FutureSourceFile, program: Pr
// 4. In --module nodenext, assume we're not emitting JS -> JS, so use
// whatever syntax Node expects based on the detected module kind
if (sourceFile.impliedNodeFormat === ModuleKind.CommonJS) return true;
if (sourceFile.impliedNodeFormat === ModuleKind.ESNext) return false;
// TODO: consider removing `impliedNodeFormatForEmit`
if (getImpliedNodeFormatForEmit(sourceFile, program) === ModuleKind.CommonJS) return true;
if (getImpliedNodeFormatForEmit(sourceFile, program) === ModuleKind.ESNext) return false;
// 5. Match the first other JS file in the program that's unambiguously CJS or ESM
for (const otherFile of program.getSourceFiles()) {
@@ -1186,7 +1190,7 @@ function getNewImportFixes(
// `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types.
return { kind: ImportFixKind.JsdocTypeImport, moduleSpecifierKind, moduleSpecifier, usagePosition, exportInfo, isReExport: i > 0 };
}
const importKind = getImportKind(sourceFile, exportInfo.exportKind, compilerOptions);
const importKind = getImportKind(sourceFile, exportInfo.exportKind, program);
let qualification: Qualification | undefined;
if (usagePosition !== undefined && importKind === ImportKind.CommonJS && exportInfo.exportKind === ExportKind.Named) {
// Compiler options are restricting our import options to a require, but we need to access
@@ -1198,7 +1202,7 @@ function getNewImportFixes(
const exportEquals = checker.resolveExternalModuleSymbol(exportInfo.moduleSymbol);
let namespacePrefix;
if (exportEquals !== exportInfo.moduleSymbol) {
namespacePrefix = forEachNameOfDefaultExport(exportEquals, checker, compilerOptions, /*preferCapitalizedNames*/ false, identity)!;
namespacePrefix = forEachNameOfDefaultExport(exportEquals, checker, getEmitScriptTarget(compilerOptions), identity)!;
}
namespacePrefix ||= moduleSymbolToValidIdentifier(
exportInfo.moduleSymbol,
@@ -1417,8 +1421,8 @@ function getUmdSymbol(token: Node, checker: TypeChecker): Symbol | undefined {
*
* @internal
*/
export function getImportKind(importingFile: SourceFile | FutureSourceFile, exportKind: ExportKind, compilerOptions: CompilerOptions, forceImportKeyword?: boolean): ImportKind {
if (compilerOptions.verbatimModuleSyntax && (getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS || importingFile.impliedNodeFormat === ModuleKind.CommonJS)) {
export function getImportKind(importingFile: SourceFile | FutureSourceFile, exportKind: ExportKind, program: Program, forceImportKeyword?: boolean): ImportKind {
if (program.getCompilerOptions().verbatimModuleSyntax && getEmitModuleFormatOfFile(importingFile, program) === 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;
}
@@ -1428,22 +1432,22 @@ export function getImportKind(importingFile: SourceFile | FutureSourceFile, expo
case ExportKind.Default:
return ImportKind.Default;
case ExportKind.ExportEquals:
return getExportEqualsImportKind(importingFile, compilerOptions, !!forceImportKeyword);
return getExportEqualsImportKind(importingFile, program.getCompilerOptions(), !!forceImportKeyword);
case ExportKind.UMD:
return getUmdImportKind(importingFile, compilerOptions, !!forceImportKeyword);
return getUmdImportKind(importingFile, program, !!forceImportKeyword);
default:
return Debug.assertNever(exportKind);
}
}
function getUmdImportKind(importingFile: SourceFile | FutureSourceFile, compilerOptions: CompilerOptions, forceImportKeyword: boolean): ImportKind {
function getUmdImportKind(importingFile: SourceFile | FutureSourceFile, program: Program, forceImportKeyword: boolean): ImportKind {
// Import a synthetic `default` if enabled.
if (getAllowSyntheticDefaultImports(compilerOptions)) {
if (getAllowSyntheticDefaultImports(program.getCompilerOptions())) {
return ImportKind.Default;
}
// When a synthetic `default` is unavailable, use `import..require` if the module kind supports it.
const moduleKind = getEmitModuleKind(compilerOptions);
const moduleKind = getEmitModuleKind(program.getCompilerOptions());
switch (moduleKind) {
case ModuleKind.AMD:
case ModuleKind.CommonJS:
@@ -1463,7 +1467,7 @@ function getUmdImportKind(importingFile: SourceFile | FutureSourceFile, compiler
return ImportKind.Namespace;
case ModuleKind.Node16:
case ModuleKind.NodeNext:
return importingFile.impliedNodeFormat === ModuleKind.ESNext ? ImportKind.Namespace : ImportKind.CommonJS;
return getImpliedNodeFormatForEmit(importingFile, program) === ModuleKind.ESNext ? ImportKind.Namespace : ImportKind.CommonJS;
default:
return Debug.assertNever(moduleKind, `Unexpected moduleKind ${moduleKind}`);
}
@@ -1555,7 +1559,7 @@ function getExportInfos(
if (
defaultInfo
&& symbolFlagsHaveMeaning(checker.getSymbolFlags(defaultInfo.symbol), currentTokenMeaning)
&& forEachNameOfDefaultExport(defaultInfo.symbol, checker, compilerOptions, isJsxTagName, name => name === symbolName)
&& forEachNameOfDefaultExport(defaultInfo.symbol, checker, getEmitScriptTarget(compilerOptions), (name, capitalizedName) => (isJsxTagName ? capitalizedName ?? name : name) === symbolName)
) {
addSymbol(moduleSymbol, sourceFile, defaultInfo.symbol, defaultInfo.exportKind, program, isFromPackageJson);
}
@@ -2038,3 +2042,11 @@ function symbolFlagsHaveMeaning(flags: SymbolFlags, meaning: SemanticMeaning): b
meaning & SemanticMeaning.Namespace ? !!(flags & SymbolFlags.Namespace) :
false;
}
function getImpliedNodeFormatForEmit(file: SourceFile | FutureSourceFile, program: Program) {
return isFullSourceFile(file) ? program.getImpliedNodeFormatForEmit(file) : getImpliedNodeFormatForEmitWorker(file, program.getCompilerOptions());
}
function getEmitModuleFormatOfFile(file: SourceFile | FutureSourceFile, program: Program) {
return isFullSourceFile(file) ? program.getEmitModuleFormatOfFile(file) : getEmitModuleFormatOfFileWorker(file, program.getCompilerOptions());
}
+9 -3
View File
@@ -1161,11 +1161,13 @@ function getJSDocParamAnnotation(
? createSnippetPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
})
: createPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
});
setEmitFlags(typeNode, EmitFlags.SingleLine);
@@ -1459,6 +1461,7 @@ function getExhaustiveCaseSnippets(
const printer = createSnippetPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
newLine: getNewLineKind(newLineChar),
});
@@ -1720,7 +1723,7 @@ function createCompletionEntry(
if (originIsResolvedExport(origin)) {
sourceDisplay = [textPart(origin.moduleSpecifier)];
if (importStatementCompletion) {
({ insertText, replacementSpan } = getInsertTextAndReplacementSpanForImportCompletion(name, importStatementCompletion, origin, useSemicolons, sourceFile, options, preferences));
({ insertText, replacementSpan } = getInsertTextAndReplacementSpanForImportCompletion(name, importStatementCompletion, origin, useSemicolons, sourceFile, program, preferences));
isSnippet = preferences.includeCompletionsWithSnippetText ? true : undefined;
}
}
@@ -1978,6 +1981,7 @@ function getEntryForMemberCompletion(
const printer = createSnippetPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
omitTrailingSemicolon: false,
newLine: getNewLineKind(getNewLineOrDefaultFromHost(host, formatContext?.options)),
@@ -2204,6 +2208,7 @@ function getEntryForObjectLiteralMethodCompletion(
const printer = createSnippetPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
omitTrailingSemicolon: false,
newLine: getNewLineKind(getNewLineOrDefaultFromHost(host, formatContext?.options)),
@@ -2218,6 +2223,7 @@ function getEntryForObjectLiteralMethodCompletion(
const signaturePrinter = createPrinter({
removeComments: true,
module: options.module,
moduleResolution: options.moduleResolution,
target: options.target,
omitTrailingSemicolon: true,
});
@@ -2520,14 +2526,14 @@ function completionEntryDataToSymbolOriginInfo(data: CompletionEntryData, comple
return unresolvedOrigin;
}
function getInsertTextAndReplacementSpanForImportCompletion(name: string, importStatementCompletion: ImportStatementCompletionInfo, origin: SymbolOriginInfoResolvedExport, useSemicolons: boolean, sourceFile: SourceFile, options: CompilerOptions, preferences: UserPreferences) {
function getInsertTextAndReplacementSpanForImportCompletion(name: string, importStatementCompletion: ImportStatementCompletionInfo, origin: SymbolOriginInfoResolvedExport, useSemicolons: boolean, sourceFile: SourceFile, program: Program, preferences: UserPreferences) {
const replacementSpan = importStatementCompletion.replacementSpan;
const quotedModuleSpecifier = escapeSnippetText(quote(sourceFile, preferences, origin.moduleSpecifier));
const exportKind = origin.isDefaultExport ? ExportKind.Default :
origin.exportName === InternalSymbolName.ExportEquals ? ExportKind.ExportEquals :
ExportKind.Named;
const tabStop = preferences.includeCompletionsWithSnippetText ? "$1" : "";
const importKind = codefix.getImportKind(sourceFile, exportKind, options, /*forceImportKeyword*/ true);
const importKind = codefix.getImportKind(sourceFile, exportKind, program, /*forceImportKeyword*/ true);
const isImportSpecifierTypeOnly = importStatementCompletion.couldBeTypeOnlyImportSpecifier;
const topLevelTypeOnlyText = importStatementCompletion.isTopLevelTypeOnly ? ` ${tokenToString(SyntaxKind.TypeKeyword)} ` : " ";
const importSpecifierTypeOnlyText = isImportSpecifierTypeOnly ? `${tokenToString(SyntaxKind.TypeKeyword)} ` : "";
+16 -6
View File
@@ -4,7 +4,6 @@ import {
append,
arrayIsEqualTo,
CancellationToken,
CompilerOptions,
consumesNodeCoreModules,
createMultiMap,
Debug,
@@ -18,9 +17,7 @@ import {
GetCanonicalFileName,
getDefaultLikeExportNameFromDeclaration,
getDirectoryPath,
getEmitScriptTarget,
getLocalSymbolForExportDefault,
getNamesForExportedSymbol,
getNodeModulePathParts,
getPackageNameFromTypesPackageName,
getRegexFromPattern,
@@ -46,6 +43,7 @@ import {
Path,
pathContainsNodeModules,
Program,
ScriptTarget,
skipAlias,
SourceFile,
startsWith,
@@ -198,7 +196,7 @@ export function createCacheableExportInfoMap(host: CacheableExportInfoMapHost):
// get a better name.
const names = exportKind === ExportKind.Named || isExternalModuleSymbol(namedSymbol)
? unescapeLeadingUnderscores(symbolTableKey)
: getNamesForExportedSymbol(namedSymbol, /*scriptTarget*/ undefined);
: getNamesForExportedSymbol(namedSymbol, checker, /*scriptTarget*/ undefined);
const symbolName = typeof names === "string" ? names : names[0];
const capitalizedSymbolName = typeof names === "string" ? undefined : names[1];
@@ -572,12 +570,21 @@ function isImportableSymbol(symbol: Symbol, checker: TypeChecker) {
return !checker.isUndefinedSymbol(symbol) && !checker.isUnknownSymbol(symbol) && !isKnownSymbol(symbol) && !isPrivateIdentifierSymbol(symbol);
}
function getNamesForExportedSymbol(defaultExport: Symbol, checker: TypeChecker, scriptTarget: ScriptTarget | undefined) {
let names: string | string[] | undefined;
forEachNameOfDefaultExport(defaultExport, checker, scriptTarget, (name, capitalizedName) => {
names = capitalizedName ? [name, capitalizedName] : name;
return true;
});
return Debug.checkDefined(names);
}
/**
* @internal
* May call `cb` multiple times with the same name.
* Terminates when `cb` returns a truthy value.
*/
export function forEachNameOfDefaultExport<T>(defaultExport: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions, preferCapitalizedNames: boolean, cb: (name: string) => T | undefined): T | undefined {
export function forEachNameOfDefaultExport<T>(defaultExport: Symbol, checker: TypeChecker, scriptTarget: ScriptTarget | undefined, cb: (name: string, capitalizedName?: string) => T | undefined): T | undefined {
let chain: Symbol[] | undefined;
let current: Symbol | undefined = defaultExport;
const seen = new Map<Symbol, true>();
@@ -604,7 +611,10 @@ export function forEachNameOfDefaultExport<T>(defaultExport: Symbol, checker: Ty
for (const symbol of chain ?? emptyArray) {
if (symbol.parent && isExternalModuleSymbol(symbol.parent)) {
const final = cb(moduleSymbolToValidIdentifier(symbol.parent, getEmitScriptTarget(compilerOptions), preferCapitalizedNames));
const final = cb(
moduleSymbolToValidIdentifier(symbol.parent, scriptTarget, /*forceCapitalize*/ false),
moduleSymbolToValidIdentifier(symbol.parent, scriptTarget, /*forceCapitalize*/ true),
);
if (final) return final;
}
}
+42 -30
View File
@@ -200,7 +200,7 @@ export function getStringLiteralCompletions(
includeSymbol: boolean,
): CompletionInfo | undefined {
if (isInReferenceComment(sourceFile, position)) {
const entries = getTripleSlashReferenceCompletion(sourceFile, position, options, host);
const entries = getTripleSlashReferenceCompletion(sourceFile, position, program, host);
return entries && convertPathCompletions(entries);
}
if (isInString(sourceFile, position, contextToken)) {
@@ -614,8 +614,8 @@ function getStringLiteralCompletionsFromModuleNamesWorker(sourceFile: SourceFile
const extensionOptions = getExtensionOptions(compilerOptions, ReferenceKind.ModuleSpecifier, sourceFile, typeChecker, preferences, mode);
return isPathRelativeToScript(literalValue) || !compilerOptions.baseUrl && !compilerOptions.paths && (isRootedDiskPath(literalValue) || isUrl(literalValue))
? getCompletionEntriesForRelativeModules(literalValue, scriptDirectory, compilerOptions, host, scriptPath, extensionOptions)
: getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, mode, compilerOptions, host, extensionOptions, typeChecker);
? getCompletionEntriesForRelativeModules(literalValue, scriptDirectory, program, host, scriptPath, extensionOptions)
: getCompletionEntriesForNonRelativeModules(literalValue, scriptDirectory, mode, program, host, extensionOptions);
}
interface ExtensionOptions {
@@ -635,20 +635,21 @@ function getExtensionOptions(compilerOptions: CompilerOptions, referenceKind: Re
resolutionMode,
};
}
function getCompletionEntriesForRelativeModules(literalValue: string, scriptDirectory: string, compilerOptions: CompilerOptions, host: LanguageServiceHost, scriptPath: Path, extensionOptions: ExtensionOptions) {
function getCompletionEntriesForRelativeModules(literalValue: string, scriptDirectory: string, program: Program, host: LanguageServiceHost, scriptPath: Path, extensionOptions: ExtensionOptions) {
const compilerOptions = program.getCompilerOptions();
if (compilerOptions.rootDirs) {
return getCompletionEntriesForDirectoryFragmentWithRootDirs(
compilerOptions.rootDirs,
literalValue,
scriptDirectory,
extensionOptions,
compilerOptions,
program,
host,
scriptPath,
);
}
else {
return arrayFrom(getCompletionEntriesForDirectoryFragment(literalValue, scriptDirectory, extensionOptions, host, /*moduleSpecifierIsRelative*/ true, scriptPath).values());
return arrayFrom(getCompletionEntriesForDirectoryFragment(literalValue, scriptDirectory, extensionOptions, program, host, /*moduleSpecifierIsRelative*/ true, scriptPath).values());
}
}
@@ -686,12 +687,13 @@ function getBaseDirectoriesFromRootDirs(rootDirs: string[], basePath: string, sc
);
}
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptDirectory: string, extensionOptions: ExtensionOptions, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude: string): readonly NameAndKind[] {
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptDirectory: string, extensionOptions: ExtensionOptions, program: Program, host: LanguageServiceHost, exclude: string): readonly NameAndKind[] {
const compilerOptions = program.getCompilerOptions();
const basePath = compilerOptions.project || host.getCurrentDirectory();
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptDirectory, ignoreCase);
return deduplicate<NameAndKind>(
flatMap(baseDirectories, baseDirectory => arrayFrom(getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensionOptions, host, /*moduleSpecifierIsRelative*/ true, exclude).values())),
flatMap(baseDirectories, baseDirectory => arrayFrom(getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensionOptions, program, host, /*moduleSpecifierIsRelative*/ true, exclude).values())),
(itemA, itemB) => itemA.name === itemB.name && itemA.kind === itemB.kind && itemA.extension === itemB.extension,
);
}
@@ -707,6 +709,7 @@ function getCompletionEntriesForDirectoryFragment(
fragment: string,
scriptDirectory: string,
extensionOptions: ExtensionOptions,
program: Program,
host: LanguageServiceHost,
moduleSpecifierIsRelative: boolean,
exclude?: string,
@@ -746,7 +749,7 @@ function getCompletionEntriesForDirectoryFragment(
if (versionPaths) {
const packageDirectory = getDirectoryPath(packageJsonPath);
const pathInPackage = absolutePath.slice(ensureTrailingDirectorySeparator(packageDirectory).length);
if (addCompletionEntriesFromPaths(result, pathInPackage, packageDirectory, extensionOptions, host, versionPaths)) {
if (addCompletionEntriesFromPaths(result, pathInPackage, packageDirectory, extensionOptions, program, host, versionPaths)) {
// A true result means one of the `versionPaths` was matched, which will block relative resolution
// to files and folders from here. All reachable paths given the pattern match are already added.
return result;
@@ -769,7 +772,7 @@ function getCompletionEntriesForDirectoryFragment(
continue;
}
const { name, extension } = getFilenameWithExtensionOption(getBaseFileName(filePath), host.getCompilationSettings(), extensionOptions, /*isExportsWildcard*/ false);
const { name, extension } = getFilenameWithExtensionOption(getBaseFileName(filePath), program, extensionOptions, /*isExportsWildcard*/ false);
result.add(nameAndKind(name, ScriptElementKind.scriptElement, extension));
}
}
@@ -789,7 +792,7 @@ function getCompletionEntriesForDirectoryFragment(
return result;
}
function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerOptions, extensionOptions: ExtensionOptions, isExportsWildcard: boolean): { name: string; extension: Extension | undefined; } {
function getFilenameWithExtensionOption(name: string, program: Program, extensionOptions: ExtensionOptions, isExportsWildcard: boolean): { name: string; extension: Extension | undefined; } {
const nonJsResult = moduleSpecifiers.tryGetRealFileNameForNonJsDeclarationFileName(name);
if (nonJsResult) {
return { name: nonJsResult, extension: tryGetExtensionFromPath(nonJsResult) };
@@ -800,7 +803,8 @@ function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerO
let allowedEndings = getModuleSpecifierPreferences(
{ importModuleSpecifierEnding: extensionOptions.endingPreference },
compilerOptions,
program,
program.getCompilerOptions(),
extensionOptions.importingSourceFile,
).getAllowedEndingsInPreferredOrder(extensionOptions.resolutionMode);
@@ -814,7 +818,7 @@ function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerO
if (fileExtensionIsOneOf(name, supportedTSImplementationExtensions)) {
return { name, extension: tryGetExtensionFromPath(name) };
}
const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(name, compilerOptions);
const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(name, program.getCompilerOptions());
return outputExtension
? { name: changeExtension(name, outputExtension), extension: outputExtension }
: { name, extension: tryGetExtensionFromPath(name) };
@@ -828,7 +832,7 @@ function getFilenameWithExtensionOption(name: string, compilerOptions: CompilerO
return { name: removeFileExtension(name), extension: tryGetExtensionFromPath(name) };
}
const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(name, compilerOptions);
const outputExtension = moduleSpecifiers.tryGetJSExtensionForFile(name, program.getCompilerOptions());
return outputExtension
? { name: changeExtension(name, outputExtension), extension: outputExtension }
: { name, extension: tryGetExtensionFromPath(name) };
@@ -840,6 +844,7 @@ function addCompletionEntriesFromPaths(
fragment: string,
baseDirectory: string,
extensionOptions: ExtensionOptions,
program: Program,
host: LanguageServiceHost,
paths: MapLike<string[]>,
) {
@@ -851,7 +856,7 @@ function addCompletionEntriesFromPaths(
const lengthB = typeof patternB === "object" ? patternB.prefix.length : b.length;
return compareValues(lengthB, lengthA);
};
return addCompletionEntriesFromPathsOrExports(result, /*isExports*/ false, fragment, baseDirectory, extensionOptions, host, getOwnKeys(paths), getPatternsForKey, comparePaths);
return addCompletionEntriesFromPathsOrExports(result, /*isExports*/ false, fragment, baseDirectory, extensionOptions, program, host, getOwnKeys(paths), getPatternsForKey, comparePaths);
}
/** @returns whether `fragment` was a match for any `paths` (which should indicate whether any other path completions should be offered) */
@@ -861,6 +866,7 @@ function addCompletionEntriesFromPathsOrExports(
fragment: string,
baseDirectory: string,
extensionOptions: ExtensionOptions,
program: Program,
host: LanguageServiceHost,
keys: readonly string[],
getPatternsForKey: (key: string) => string[] | undefined,
@@ -895,7 +901,7 @@ function addCompletionEntriesFromPathsOrExports(
if (typeof pathPattern === "string" || matchedPath === undefined || comparePaths(key, matchedPath) !== Comparison.GreaterThan) {
pathResults.push({
matchedPattern: isMatch,
results: getCompletionsForPathMapping(keyWithoutLeadingDotSlash, patterns, fragment, baseDirectory, extensionOptions, isExports && isMatch, host)
results: getCompletionsForPathMapping(keyWithoutLeadingDotSlash, patterns, fragment, baseDirectory, extensionOptions, isExports && isMatch, program, host)
.map(({ name, kind, extension }) => nameAndKind(name, kind, extension)),
});
}
@@ -917,11 +923,12 @@ function getCompletionEntriesForNonRelativeModules(
fragment: string,
scriptPath: string,
mode: ResolutionMode,
compilerOptions: CompilerOptions,
program: Program,
host: LanguageServiceHost,
extensionOptions: ExtensionOptions,
typeChecker: TypeChecker,
): readonly NameAndKind[] {
const typeChecker = program.getTypeChecker();
const compilerOptions = program.getCompilerOptions();
const { baseUrl, paths } = compilerOptions;
const result = createNameAndKindSet();
@@ -929,12 +936,12 @@ function getCompletionEntriesForNonRelativeModules(
if (baseUrl) {
const absolute = normalizePath(combinePaths(host.getCurrentDirectory(), baseUrl));
getCompletionEntriesForDirectoryFragment(fragment, absolute, extensionOptions, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
getCompletionEntriesForDirectoryFragment(fragment, absolute, extensionOptions, program, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
}
if (paths) {
const absolute = getPathsBasePath(compilerOptions, host)!;
addCompletionEntriesFromPaths(result, fragment, absolute, extensionOptions, host, paths);
addCompletionEntriesFromPaths(result, fragment, absolute, extensionOptions, program, host, paths);
}
const fragmentDirectory = getFragmentDirectory(fragment);
@@ -942,7 +949,7 @@ function getCompletionEntriesForNonRelativeModules(
result.add(nameAndKind(ambientName, ScriptElementKind.externalModuleName, /*extension*/ undefined));
}
getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, fragmentDirectory, extensionOptions, result);
getCompletionEntriesFromTypings(host, program, scriptPath, fragmentDirectory, extensionOptions, result);
if (moduleResolutionUsesNodeModules(moduleResolution)) {
// If looking for a global package name, don't just include everything in `node_modules` because that includes dependencies' own dependencies.
@@ -961,7 +968,7 @@ function getCompletionEntriesForNonRelativeModules(
let ancestorLookup: (directory: string) => void | undefined = ancestor => {
const nodeModules = combinePaths(ancestor, "node_modules");
if (tryDirectoryExists(host, nodeModules)) {
getCompletionEntriesForDirectoryFragment(fragment, nodeModules, extensionOptions, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
getCompletionEntriesForDirectoryFragment(fragment, nodeModules, extensionOptions, program, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
}
};
if (fragmentDirectory && getResolvePackageJsonExports(compilerOptions)) {
@@ -998,6 +1005,7 @@ function getCompletionEntriesForNonRelativeModules(
fragmentSubpath,
packageDirectory,
extensionOptions,
program,
host,
keys,
key => singleElementArray(getPatternFromFirstMatchingCondition(exports[key], conditions)),
@@ -1041,6 +1049,7 @@ function getCompletionsForPathMapping(
packageDirectory: string,
extensionOptions: ExtensionOptions,
isExportsWildcard: boolean,
program: Program,
host: LanguageServiceHost,
): readonly NameAndKind[] {
if (!endsWith(path, "*")) {
@@ -1052,9 +1061,9 @@ function getCompletionsForPathMapping(
const remainingFragment = tryRemovePrefix(fragment, pathPrefix);
if (remainingFragment === undefined) {
const starIsFullPathComponent = path[path.length - 2] === "/";
return starIsFullPathComponent ? justPathMappingName(pathPrefix, ScriptElementKind.directory) : flatMap(patterns, pattern => getModulesForPathsPattern("", packageDirectory, pattern, extensionOptions, isExportsWildcard, host)?.map(({ name, ...rest }) => ({ name: pathPrefix + name, ...rest })));
return starIsFullPathComponent ? justPathMappingName(pathPrefix, ScriptElementKind.directory) : flatMap(patterns, pattern => getModulesForPathsPattern("", packageDirectory, pattern, extensionOptions, isExportsWildcard, program, host)?.map(({ name, ...rest }) => ({ name: pathPrefix + name, ...rest })));
}
return flatMap(patterns, pattern => getModulesForPathsPattern(remainingFragment, packageDirectory, pattern, extensionOptions, isExportsWildcard, host));
return flatMap(patterns, pattern => getModulesForPathsPattern(remainingFragment, packageDirectory, pattern, extensionOptions, isExportsWildcard, program, host));
function justPathMappingName(name: string, kind: ScriptElementKind.directory | ScriptElementKind.scriptElement): readonly NameAndKind[] {
return startsWith(name, fragment) ? [{ name: removeTrailingDirectorySeparator(name), kind, extension: undefined }] : emptyArray;
@@ -1067,6 +1076,7 @@ function getModulesForPathsPattern(
pattern: string,
extensionOptions: ExtensionOptions,
isExportsWildcard: boolean,
program: Program,
host: LanguageServiceHost,
): readonly NameAndKind[] | undefined {
if (!host.readDirectory) {
@@ -1115,7 +1125,7 @@ function getModulesForPathsPattern(
if (containsSlash(trimmedWithPattern)) {
return directoryResult(getPathComponents(removeLeadingDirectorySeparator(trimmedWithPattern))[1]);
}
const { name, extension } = getFilenameWithExtensionOption(trimmedWithPattern, host.getCompilationSettings(), extensionOptions, isExportsWildcard);
const { name, extension } = getFilenameWithExtensionOption(trimmedWithPattern, program, extensionOptions, isExportsWildcard);
return nameAndKind(name, ScriptElementKind.scriptElement, extension);
}
});
@@ -1159,7 +1169,8 @@ function getAmbientModuleCompletions(fragment: string, fragmentDirectory: string
return nonRelativeModuleNames;
}
function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): readonly PathCompletion[] | undefined {
function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, program: Program, host: LanguageServiceHost): readonly PathCompletion[] | undefined {
const compilerOptions = program.getCompilerOptions();
const token = getTokenAtPosition(sourceFile, position);
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
const range = commentRanges && find(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end);
@@ -1174,13 +1185,14 @@ function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: num
const [, prefix, kind, toComplete] = match;
const scriptPath = getDirectoryPath(sourceFile.path);
const names = kind === "path" ? getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getExtensionOptions(compilerOptions, ReferenceKind.Filename, sourceFile), host, /*moduleSpecifierIsRelative*/ true, sourceFile.path)
: kind === "types" ? getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, getFragmentDirectory(toComplete), getExtensionOptions(compilerOptions, ReferenceKind.ModuleSpecifier, sourceFile))
const names = kind === "path" ? getCompletionEntriesForDirectoryFragment(toComplete, scriptPath, getExtensionOptions(compilerOptions, ReferenceKind.Filename, sourceFile), program, host, /*moduleSpecifierIsRelative*/ true, sourceFile.path)
: kind === "types" ? getCompletionEntriesFromTypings(host, program, scriptPath, getFragmentDirectory(toComplete), getExtensionOptions(compilerOptions, ReferenceKind.ModuleSpecifier, sourceFile))
: Debug.fail();
return addReplacementSpans(toComplete, range.pos + prefix.length, arrayFrom(names.values()));
}
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, fragmentDirectory: string | undefined, extensionOptions: ExtensionOptions, result = createNameAndKindSet()): NameAndKindSet {
function getCompletionEntriesFromTypings(host: LanguageServiceHost, program: Program, scriptPath: string, fragmentDirectory: string | undefined, extensionOptions: ExtensionOptions, result = createNameAndKindSet()): NameAndKindSet {
const options = program.getCompilerOptions();
// Check for typings specified in compiler options
const seen = new Map<string, true>();
@@ -1215,7 +1227,7 @@ function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: Com
const baseDirectory = combinePaths(directory, typeDirectoryName);
const remainingFragment = tryRemoveDirectoryPrefix(fragmentDirectory, packageName, hostGetCanonicalFileName(host));
if (remainingFragment !== undefined) {
getCompletionEntriesForDirectoryFragment(remainingFragment, baseDirectory, extensionOptions, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
getCompletionEntriesForDirectoryFragment(remainingFragment, baseDirectory, extensionOptions, program, host, /*moduleSpecifierIsRelative*/ false, /*exclude*/ undefined, result);
}
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ export function computeSuggestionDiagnostics(sourceFile: SourceFile, program: Pr
program.getSemanticDiagnostics(sourceFile, cancellationToken);
const diags: DiagnosticWithLocation[] = [];
const checker = program.getTypeChecker();
const isCommonJSFile = sourceFile.impliedNodeFormat === ModuleKind.CommonJS || fileExtensionIsOneOf(sourceFile.fileName, [Extension.Cts, Extension.Cjs]);
const isCommonJSFile = program.getImpliedNodeFormatForEmit(sourceFile) === ModuleKind.CommonJS || fileExtensionIsOneOf(sourceFile.fileName, [Extension.Cts, Extension.Cjs]);
if (
!isCommonJSFile &&
+20 -24
View File
@@ -95,6 +95,7 @@ import {
getEmitModuleKind,
getEmitScriptTarget,
getExternalModuleImportEqualsDeclarationExpression,
getImpliedNodeFormatForEmitWorker,
getImpliedNodeFormatForFile,
getImpliedNodeFormatForFileWorker,
getIndentString,
@@ -2476,6 +2477,8 @@ export function createModuleSpecifierResolutionHost(program: Program, host: Lang
getNearestAncestorDirectoryWithPackageJson: maybeBind(host, host.getNearestAncestorDirectoryWithPackageJson),
getFileIncludeReasons: () => program.getFileIncludeReasons(),
getCommonSourceDirectory: () => program.getCommonSourceDirectory(),
getDefaultResolutionModeForFile: file => program.getDefaultResolutionModeForFile(file),
getModeForResolutionAtIndex: (file, index) => program.getModeForResolutionAtIndex(file, index),
};
}
@@ -3987,22 +3990,13 @@ export function firstOrOnly<T>(valueOrArray: T | readonly T[]): T {
return isArray(valueOrArray) ? first(valueOrArray) : valueOrArray;
}
/** @internal */
export function getNamesForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget | undefined): string | [lowercase: string, capitalized: string] {
if (needsNameFromDeclaration(symbol)) {
const fromDeclaration = getDefaultLikeExportNameFromDeclaration(symbol);
if (fromDeclaration) return fromDeclaration;
const fileNameCase = moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ false);
const capitalized = moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ true);
if (fileNameCase === capitalized) return fileNameCase;
return [fileNameCase, capitalized];
}
return symbol.name;
}
/** @internal */
/**
* If a type checker and multiple files are available, consider using `forEachNameOfDefaultExport`
* instead, which searches for names of re-exported defaults/namespaces in target files.
* @internal
*/
export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTarget | undefined, preferCapitalized?: boolean) {
if (needsNameFromDeclaration(symbol)) {
if (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default) {
// Names for default exports:
// - export default foo => foo
// - export { foo as default } => foo
@@ -4013,11 +4007,11 @@ export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTar
return symbol.name;
}
function needsNameFromDeclaration(symbol: Symbol) {
return !(symbol.flags & SymbolFlags.Transient) && (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default);
}
/** @internal */
/**
* If a type checker and multiple files are available, consider using `forEachNameOfDefaultExport`
* instead, which searches for names of re-exported defaults/namespaces in target files.
* @internal
*/
export function getDefaultLikeExportNameFromDeclaration(symbol: Symbol): string | undefined {
return firstDefined(symbol.declarations, d => {
// "export default" in this case. See `ExportAssignment`for more details.
@@ -4250,11 +4244,13 @@ export function fileShouldUseJavaScriptRequire(file: SourceFile | string, progra
if (!hasJSFileExtension(fileName)) {
return false;
}
const compilerOptions = program.getCompilerOptions();
const compilerOptions = typeof file === "string" ? program.getCompilerOptions() : program.getCompilerOptionsForFile(file);
const moduleKind = getEmitModuleKind(compilerOptions);
const impliedNodeFormat = typeof file === "string"
? getImpliedNodeFormatForFile(toPath(file, host.getCurrentDirectory(), hostGetCanonicalFileName(host)), program.getPackageJsonInfoCache?.(), host, compilerOptions)
: file.impliedNodeFormat;
const sourceFileLike = typeof file === "string" ? {
fileName: file,
impliedNodeFormat: getImpliedNodeFormatForFile(toPath(file, host.getCurrentDirectory(), hostGetCanonicalFileName(host)), program.getPackageJsonInfoCache?.(), host, compilerOptions),
} : file;
const impliedNodeFormat = getImpliedNodeFormatForEmitWorker(sourceFileLike, compilerOptions);
if (impliedNodeFormat === ModuleKind.ESNext) {
return false;