Refactor default export info name gathering (#58460)

This commit is contained in:
Andrew Branch
2024-05-08 09:33:17 -07:00
committed by GitHub
parent b9c71c3fc3
commit bc14459f3b
11 changed files with 206 additions and 118 deletions
+1
View File
@@ -1900,6 +1900,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
getMemberOverrideModifierStatus,
isTypeParameterPossiblyReferenced,
typeHasCallOrConstructSignatures,
getSymbolFlags,
};
function getCandidateSignaturesForStringLiteralCompletions(call: CallLikeExpression, editingArgument: Node) {
+1
View File
@@ -5361,6 +5361,7 @@ export interface TypeChecker {
/** @internal */ getMemberOverrideModifierStatus(node: ClassLikeDeclaration, member: ClassElement, memberSymbol: Symbol): MemberOverrideStatus;
/** @internal */ isTypeParameterPossiblyReferenced(tp: TypeParameter, node: Node): boolean;
/** @internal */ typeHasCallOrConstructSignatures(type: Type): boolean;
/** @internal */ getSymbolFlags(symbol: Symbol): SymbolFlags;
}
/** @internal */
+1 -1
View File
@@ -1,6 +1,5 @@
import {
createCodeFixActionWithoutFixAll,
moduleSpecifierToValidIdentifier,
registerCodeFix,
} from "../_namespaces/ts.codefix.js";
import {
@@ -59,6 +58,7 @@ import {
mapIterator,
MethodDeclaration,
Modifier,
moduleSpecifierToValidIdentifier,
Node,
NodeArray,
NodeFlags,
+18 -52
View File
@@ -41,12 +41,12 @@ import {
flatMap,
flatMapIterator,
forEachExternalModuleToImportFrom,
forEachNameOfDefaultExport,
formatting,
FutureSourceFile,
FutureSymbolExportInfo,
getAllowSyntheticDefaultImports,
getBaseFileName,
getDefaultExportInfoWorker,
getDefaultLikeExportInfo,
getDirectoryPath,
getEmitModuleFormatOfFileWorker,
@@ -55,7 +55,6 @@ import {
getEmitScriptTarget,
getExportInfoMap,
getImpliedNodeFormatForEmitWorker,
getMeaningFromDeclaration,
getMeaningFromLocation,
getNameForExportedSymbol,
getOutputExtension,
@@ -71,6 +70,7 @@ import {
hasJSFileExtension,
hostGetCanonicalFileName,
Identifier,
identity,
ImportClause,
ImportEqualsDeclaration,
importFromModuleSpecifier,
@@ -81,8 +81,6 @@ import {
isExternalModuleReference,
isFullSourceFile,
isIdentifier,
isIdentifierPart,
isIdentifierStart,
isImportableFile,
isImportDeclaration,
isImportEqualsDeclaration,
@@ -96,7 +94,6 @@ import {
isNamespaceImport,
isRequireVariableStatement,
isSourceFileJS,
isStringANonContextualKeyword,
isStringLiteral,
isStringLiteralLike,
isTypeOnlyImportDeclaration,
@@ -114,6 +111,7 @@ import {
ModuleKind,
moduleResolutionUsesNodeModules,
moduleSpecifiers,
moduleSymbolToValidIdentifier,
MultiMap,
Mutable,
NamedImports,
@@ -129,12 +127,9 @@ import {
pathIsBareSpecifier,
Program,
QuotePreference,
removeFileExtension,
removeSuffix,
RequireOrImportCall,
RequireVariableStatement,
sameMap,
ScriptTarget,
SemanticMeaning,
shouldUseUriStyleNodeCoreModules,
single,
@@ -873,7 +868,6 @@ function getAllExportInfoForSymbol(importingFile: SourceFile | FutureSourceFile,
}
function getSingleExportInfoForSymbol(symbol: Symbol, symbolName: string, moduleSymbol: Symbol, program: Program, host: LanguageServiceHost): SymbolExportInfo {
const compilerOptions = program.getCompilerOptions();
const mainProgramInfo = getInfoWithChecker(program.getTypeChecker(), /*isFromPackageJson*/ false);
if (mainProgramInfo) {
return mainProgramInfo;
@@ -882,7 +876,7 @@ function getSingleExportInfoForSymbol(symbol: Symbol, symbolName: string, module
return Debug.checkDefined(autoImportProvider && getInfoWithChecker(autoImportProvider, /*isFromPackageJson*/ true), `Could not find symbol in specified module for code actions`);
function getInfoWithChecker(checker: TypeChecker, isFromPackageJson: boolean): SymbolExportInfo | undefined {
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions);
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker);
if (defaultInfo && skipAlias(defaultInfo.symbol, checker) === symbol) {
return { symbol: defaultInfo.symbol, moduleSymbol, moduleFileName: undefined, exportKind: defaultInfo.exportKind, targetFlags: skipAlias(symbol, checker).flags, isFromPackageJson };
}
@@ -1194,7 +1188,7 @@ function getNewImportFixes(
const exportEquals = checker.resolveExternalModuleSymbol(exportInfo.moduleSymbol);
let namespacePrefix;
if (exportEquals !== exportInfo.moduleSymbol) {
namespacePrefix = getDefaultExportInfoWorker(exportEquals, checker, compilerOptions)?.name;
namespacePrefix = forEachNameOfDefaultExport(exportEquals, checker, compilerOptions, /*preferCapitalizedNames*/ false, identity)!;
}
namespacePrefix ||= moduleSymbolToValidIdentifier(
exportInfo.moduleSymbol,
@@ -1533,14 +1527,18 @@ function getExportInfos(
cancellationToken.throwIfCancellationRequested();
const compilerOptions = program.getCompilerOptions();
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions);
if (defaultInfo && (defaultInfo.name === symbolName || moduleSymbolToValidIdentifier(moduleSymbol, getEmitScriptTarget(compilerOptions), isJsxTagName) === symbolName) && symbolHasMeaning(defaultInfo.resolvedSymbol, currentTokenMeaning)) {
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker);
if (
defaultInfo
&& symbolFlagsHaveMeaning(checker.getSymbolFlags(defaultInfo.symbol), currentTokenMeaning)
&& forEachNameOfDefaultExport(defaultInfo.symbol, checker, compilerOptions, isJsxTagName, name => name === symbolName)
) {
addSymbol(moduleSymbol, sourceFile, defaultInfo.symbol, defaultInfo.exportKind, program, isFromPackageJson);
}
// check exports with the same name
const exportSymbolWithIdenticalName = checker.tryGetMemberInModuleExportsAndProperties(symbolName, moduleSymbol);
if (exportSymbolWithIdenticalName && symbolHasMeaning(exportSymbolWithIdenticalName, currentTokenMeaning)) {
if (exportSymbolWithIdenticalName && symbolFlagsHaveMeaning(checker.getSymbolFlags(exportSymbolWithIdenticalName), currentTokenMeaning)) {
addSymbol(moduleSymbol, sourceFile, exportSymbolWithIdenticalName, ExportKind.Named, program, isFromPackageJson);
}
});
@@ -2009,44 +2007,12 @@ function createConstEqualsRequireDeclaration(name: string | ObjectBindingPattern
) as RequireVariableStatement;
}
function symbolHasMeaning({ declarations }: Symbol, meaning: SemanticMeaning): boolean {
return some(declarations, decl => !!(getMeaningFromDeclaration(decl) & meaning));
}
/** @internal */
export function moduleSymbolToValidIdentifier(moduleSymbol: Symbol, target: ScriptTarget | undefined, forceCapitalize: boolean): string {
return moduleSpecifierToValidIdentifier(removeFileExtension(stripQuotes(moduleSymbol.name)), target, forceCapitalize);
}
/** @internal */
export function moduleSpecifierToValidIdentifier(moduleSpecifier: string, target: ScriptTarget | undefined, forceCapitalize?: boolean): string {
const baseName = getBaseFileName(removeSuffix(moduleSpecifier, "/index"));
let res = "";
let lastCharWasValid = true;
const firstCharCode = baseName.charCodeAt(0);
if (isIdentifierStart(firstCharCode, target)) {
res += String.fromCharCode(firstCharCode);
if (forceCapitalize) {
res = res.toUpperCase();
}
}
else {
lastCharWasValid = false;
}
for (let i = 1; i < baseName.length; i++) {
const ch = baseName.charCodeAt(i);
const isValid = isIdentifierPart(ch, target);
if (isValid) {
let char = String.fromCharCode(ch);
if (!lastCharWasValid) {
char = char.toUpperCase();
}
res += char;
}
lastCharWasValid = isValid;
}
// Need `|| "_"` to ensure result isn't empty.
return !isStringANonContextualKeyword(res) ? res || "_" : `_${res}`;
function symbolFlagsHaveMeaning(flags: SymbolFlags, meaning: SemanticMeaning): boolean {
return meaning === SemanticMeaning.All ? true :
meaning & SemanticMeaning.Value ? !!(flags & SymbolFlags.Value) :
meaning & SemanticMeaning.Type ? !!(flags & SymbolFlags.Type) :
meaning & SemanticMeaning.Namespace ? !!(flags & SymbolFlags.Namespace) :
false;
}
function getImpliedNodeFormatForEmit(file: SourceFile | FutureSourceFile, program: Program) {
+40 -56
View File
@@ -1,6 +1,7 @@
import {
__String,
addToSeen,
append,
arrayIsEqualTo,
CancellationToken,
CompilerOptions,
@@ -10,15 +11,15 @@ import {
emptyArray,
ensureTrailingDirectorySeparator,
findIndex,
firstDefined,
forEachAncestorDirectory,
forEachEntry,
FutureSourceFile,
getBaseFileName,
GetCanonicalFileName,
getDefaultLikeExportNameFromDeclaration,
getDirectoryPath,
getEmitScriptTarget,
getLocalSymbolForExportDefault,
getNameForExportedSymbol,
getNamesForExportedSymbol,
getNodeModulePathParts,
getPackageNameFromTypesPackageName,
@@ -28,12 +29,9 @@ import {
hostGetCanonicalFileName,
hostUsesCaseSensitiveFileNames,
InternalSymbolName,
isExportAssignment,
isExportSpecifier,
isExternalModuleNameRelative,
isExternalModuleSymbol,
isExternalOrCommonJsModule,
isIdentifier,
isKnownSymbol,
isNonGlobalAmbientModule,
isPrivateIdentifierSymbol,
@@ -42,13 +40,13 @@ import {
ModuleSpecifierCache,
ModuleSpecifierResolutionHost,
moduleSpecifiers,
moduleSymbolToValidIdentifier,
nodeModulesPathPart,
PackageJsonImportFilter,
Path,
pathContainsNodeModules,
Program,
skipAlias,
skipOuterExpressions,
SourceFile,
startsWith,
Statement,
@@ -56,7 +54,6 @@ import {
Symbol,
SymbolFlags,
timestamp,
tryCast,
TypeChecker,
unescapeLeadingUnderscores,
unmangleScopedPackageName,
@@ -502,14 +499,13 @@ export function getExportInfoMap(importingFile: SourceFile | FutureSourceFile, h
}
host.log?.("getExportInfoMap: cache miss or empty; calculating new results");
const compilerOptions = program.getCompilerOptions();
let moduleCount = 0;
try {
forEachExternalModuleToImportFrom(program, host, preferences, /*useAutoImportProvider*/ true, (moduleSymbol, moduleFile, program, isFromPackageJson) => {
if (++moduleCount % 100 === 0) cancellationToken?.throwIfCancellationRequested();
const seenExports = new Map<__String, true>();
const checker = program.getTypeChecker();
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions);
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker);
// Note: I think we shouldn't actually see resolved module symbols here, but weird merges
// can cause it to happen: see 'completionsImport_mergedReExport.ts'
if (defaultInfo && isImportableSymbol(defaultInfo.symbol, checker)) {
@@ -551,61 +547,49 @@ export function getExportInfoMap(importingFile: SourceFile | FutureSourceFile, h
}
/** @internal */
export function getDefaultLikeExportInfo(moduleSymbol: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions) {
const exported = getDefaultLikeExportWorker(moduleSymbol, checker);
if (!exported) return undefined;
const { symbol, exportKind } = exported;
const info = getDefaultExportInfoWorker(symbol, checker, compilerOptions);
return info && { symbol, exportKind, ...info };
}
function isImportableSymbol(symbol: Symbol, checker: TypeChecker) {
return !checker.isUndefinedSymbol(symbol) && !checker.isUnknownSymbol(symbol) && !isKnownSymbol(symbol) && !isPrivateIdentifierSymbol(symbol);
}
function getDefaultLikeExportWorker(moduleSymbol: Symbol, checker: TypeChecker): { readonly symbol: Symbol; readonly exportKind: ExportKind; } | undefined {
export function getDefaultLikeExportInfo(moduleSymbol: Symbol, checker: TypeChecker) {
const exportEquals = checker.resolveExternalModuleSymbol(moduleSymbol);
if (exportEquals !== moduleSymbol) return { symbol: exportEquals, exportKind: ExportKind.ExportEquals };
const defaultExport = checker.tryGetMemberInModuleExports(InternalSymbolName.Default, moduleSymbol);
if (defaultExport) return { symbol: defaultExport, exportKind: ExportKind.Default };
}
/** @internal */
export function getDefaultExportInfoWorker(defaultExport: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions): { readonly resolvedSymbol: Symbol; readonly name: string; } | undefined {
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
if (localSymbol) return { resolvedSymbol: localSymbol, name: localSymbol.name };
const name = getNameForExportDefault(defaultExport);
if (name !== undefined) return { resolvedSymbol: defaultExport, name };
if (defaultExport.flags & SymbolFlags.Alias) {
const aliased = checker.getImmediateAliasedSymbol(defaultExport);
if (aliased && aliased.parent) {
// - `aliased` will be undefined if the module is exporting an unresolvable name,
// but we can still offer completions for it.
// - `aliased.parent` will be undefined if the module is exporting `globalThis.something`,
// or another expression that resolves to a global.
return getDefaultExportInfoWorker(aliased, checker, compilerOptions);
}
}
if (
defaultExport.escapedName !== InternalSymbolName.Default &&
defaultExport.escapedName !== InternalSymbolName.ExportEquals
) {
return { resolvedSymbol: defaultExport, name: defaultExport.getName() };
}
return { resolvedSymbol: defaultExport, name: getNameForExportedSymbol(defaultExport, compilerOptions.target) };
function isImportableSymbol(symbol: Symbol, checker: TypeChecker) {
return !checker.isUndefinedSymbol(symbol) && !checker.isUnknownSymbol(symbol) && !isKnownSymbol(symbol) && !isPrivateIdentifierSymbol(symbol);
}
function getNameForExportDefault(symbol: Symbol): string | undefined {
return symbol.declarations && firstDefined(symbol.declarations, declaration => {
if (isExportAssignment(declaration)) {
return tryCast(skipOuterExpressions(declaration.expression), isIdentifier)?.text;
/**
* @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 {
let chain: Symbol[] | undefined;
let current: Symbol | undefined = defaultExport;
while (current) {
// The predecessor to this function also looked for a name on the `localSymbol`
// of default exports, but I think `getDefaultLikeExportNameFromDeclaration`
// accomplishes the same thing via syntax - no tests failed when I removed it.
const fromDeclaration = getDefaultLikeExportNameFromDeclaration(current);
if (fromDeclaration) {
const final = cb(fromDeclaration);
if (final) return final;
}
else if (isExportSpecifier(declaration)) {
Debug.assert(declaration.name.text === InternalSymbolName.Default, "Expected the specifier to be a default export");
return declaration.propertyName && declaration.propertyName.text;
if (current.escapedName !== InternalSymbolName.Default && current.escapedName !== InternalSymbolName.ExportEquals) {
const final = cb(current.name);
if (final) return final;
}
});
chain = append(chain, current);
current = current.flags & SymbolFlags.Alias ? checker.getImmediateAliasedSymbol(current) : undefined;
}
for (const symbol of chain ?? emptyArray) {
if (symbol.parent && isExternalModuleSymbol(symbol.parent)) {
const final = cb(moduleSymbolToValidIdentifier(symbol.parent, getEmitScriptTarget(compilerOptions), preferCapitalizedNames));
if (final) return final;
}
}
}
+2 -2
View File
@@ -1,7 +1,6 @@
import {
ApplicableRefactorInfo,
arrayFrom,
codefix,
Debug,
Diagnostics,
emptyArray,
@@ -29,6 +28,7 @@ import {
isPropertyAccessOrQualifiedName,
isShorthandPropertyAssignment,
isStringLiteral,
moduleSpecifierToValidIdentifier,
NamedImports,
NamespaceImport,
or,
@@ -222,7 +222,7 @@ export function doChangeNamedToNamespaceOrDefault(sourceFile: SourceFile, progra
toConvertSymbols.add(symbol);
}
});
const preferredName = moduleSpecifier && isStringLiteral(moduleSpecifier) ? codefix.moduleSpecifierToValidIdentifier(moduleSpecifier.text, ScriptTarget.ESNext) : "module";
const preferredName = moduleSpecifier && isStringLiteral(moduleSpecifier) ? moduleSpecifierToValidIdentifier(moduleSpecifier.text, ScriptTarget.ESNext) : "module";
function hasNamespaceNameConflict(namedImport: ImportSpecifier): boolean {
// We need to check if the preferred namespace name (`preferredName`) we'd like to use in the refactored code will present a name conflict.
// A name conflict means that, in a scope where we would like to use the preferred namespace name, there already exists a symbol with that name in that scope.
+2 -1
View File
@@ -118,6 +118,7 @@ import {
ModifierLike,
ModuleDeclaration,
ModuleKind,
moduleSpecifierToValidIdentifier,
NamedImportBindings,
Node,
NodeFlags,
@@ -392,7 +393,7 @@ function updateNamespaceLikeImport(
oldImportNode: SupportedImport,
quotePreference: QuotePreference,
): void {
const preferredNewNamespaceName = codefix.moduleSpecifierToValidIdentifier(newModuleSpecifier, ScriptTarget.ESNext);
const preferredNewNamespaceName = moduleSpecifierToValidIdentifier(newModuleSpecifier, ScriptTarget.ESNext);
let needUniqueName = false;
const toChange: Identifier[] = [];
FindAllReferences.Core.eachSymbolReferenceInFile(oldImportId, checker, sourceFile, ref => {
+49 -6
View File
@@ -20,7 +20,6 @@ import {
ClassDeclaration,
ClassExpression,
clone,
codefix,
combinePaths,
CommentKind,
CommentRange,
@@ -92,6 +91,7 @@ import {
FunctionLikeDeclaration,
FutureSourceFile,
getAssignmentDeclarationKind,
getBaseFileName,
getCombinedNodeFlagsAlwaysIncludeJSDoc,
getDirectoryPath,
getEmitModuleKind,
@@ -181,6 +181,8 @@ import {
isGlobalScopeAugmentation,
isHeritageClause,
isIdentifier,
isIdentifierPart,
isIdentifierStart,
isImportCall,
isImportClause,
isImportDeclaration,
@@ -240,6 +242,7 @@ import {
isSetAccessorDeclaration,
isSourceFile,
isSourceFileJS,
isStringANonContextualKeyword,
isStringDoubleQuoted,
isStringLiteral,
isStringLiteralLike,
@@ -319,6 +322,8 @@ import {
pseudoBigIntToString,
QualifiedName,
RefactorContext,
removeFileExtension,
removeSuffix,
Scanner,
ScriptElementKind,
ScriptElementKindModifier,
@@ -4023,8 +4028,8 @@ export function getNamesForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTa
if (needsNameFromDeclaration(symbol)) {
const fromDeclaration = getDefaultLikeExportNameFromDeclaration(symbol);
if (fromDeclaration) return fromDeclaration;
const fileNameCase = codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ false);
const capitalized = codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ true);
const fileNameCase = moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ false);
const capitalized = moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, /*forceCapitalize*/ true);
if (fileNameCase === capitalized) return fileNameCase;
return [fileNameCase, capitalized];
}
@@ -4039,7 +4044,7 @@ export function getNameForExportedSymbol(symbol: Symbol, scriptTarget: ScriptTar
// - export { foo as default } => foo
// - export default 0 => filename converted to camelCase
return getDefaultLikeExportNameFromDeclaration(symbol)
|| codefix.moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, !!preferCapitalized);
|| moduleSymbolToValidIdentifier(getSymbolParentOrFail(symbol), scriptTarget, !!preferCapitalized);
}
return symbol.name;
}
@@ -4048,7 +4053,8 @@ function needsNameFromDeclaration(symbol: Symbol) {
return !(symbol.flags & SymbolFlags.Transient) && (symbol.escapedName === InternalSymbolName.ExportEquals || symbol.escapedName === InternalSymbolName.Default);
}
function getDefaultLikeExportNameFromDeclaration(symbol: Symbol): string | undefined {
/** @internal */
export function getDefaultLikeExportNameFromDeclaration(symbol: Symbol): string | undefined {
return firstDefined(symbol.declarations, d => {
// "export default" in this case. See `ExportAssignment`for more details.
if (isExportAssignment(d)) {
@@ -4063,7 +4069,8 @@ function getDefaultLikeExportNameFromDeclaration(symbol: Symbol): string | undef
});
}
function getSymbolParentOrFail(symbol: Symbol) {
/** @internal */
export function getSymbolParentOrFail(symbol: Symbol) {
return Debug.checkDefined(
symbol.parent,
`Symbol parent was undefined. Flags: ${Debug.formatSymbolFlags(symbol.flags)}. ` +
@@ -4078,6 +4085,42 @@ function getSymbolParentOrFail(symbol: Symbol) {
);
}
/** @internal */
export function moduleSymbolToValidIdentifier(moduleSymbol: Symbol, target: ScriptTarget | undefined, forceCapitalize: boolean): string {
return moduleSpecifierToValidIdentifier(removeFileExtension(stripQuotes(moduleSymbol.name)), target, forceCapitalize);
}
/** @internal */
export function moduleSpecifierToValidIdentifier(moduleSpecifier: string, target: ScriptTarget | undefined, forceCapitalize?: boolean): string {
const baseName = getBaseFileName(removeSuffix(moduleSpecifier, "/index"));
let res = "";
let lastCharWasValid = true;
const firstCharCode = baseName.charCodeAt(0);
if (isIdentifierStart(firstCharCode, target)) {
res += String.fromCharCode(firstCharCode);
if (forceCapitalize) {
res = res.toUpperCase();
}
}
else {
lastCharWasValid = false;
}
for (let i = 1; i < baseName.length; i++) {
const ch = baseName.charCodeAt(i);
const isValid = isIdentifierPart(ch, target);
if (isValid) {
let char = String.fromCharCode(ch);
if (!lastCharWasValid) {
char = char.toUpperCase();
}
res += char;
}
lastCharWasValid = isValid;
}
// Need `|| "_"` to ensure result isn't empty.
return !isStringANonContextualKeyword(res) ? res || "_" : `_${res}`;
}
/**
* Useful to check whether a string contains another string at a specific index
* without allocating another string or traversing the entire contents of the outer string.
@@ -0,0 +1,42 @@
/// <reference path="fourslash.ts" />
// @module: nodenext
// @allowJs: true
// @Filename: /node_modules/dom7/index.d.ts
//// export interface Dom7Array {
//// length: number;
//// prop(propName: string): any;
//// }
////
//// export interface Dom7 {
//// (): Dom7Array;
//// fn: any;
//// }
////
//// declare const Dom7: Dom7;
////
//// export {
//// Dom7 as $,
//// };
// @Filename: /dom7.js
//// import * as methods from 'dom7';
//// Object.keys(methods).forEach((methodName) => {
//// if (methodName === '$') return;
//// methods.$.fn[methodName] = methods[methodName];
//// });
////
//// export default methods.$;
// @Filename: /swipe-back.js
//// import $ from './dom7.js';
//// /*1*/
verify.completions({
marker: "1",
// some kind of a check should be added here
preferences: {
includeCompletionsForModuleExports: true,
}
});
@@ -0,0 +1,41 @@
/// <reference path="fourslash.ts" />
// @module: commonjs
// @allowJs: true
// @Filename: /file1.js
//// const a = 1;
//// export {
//// a as b
//// };
//// export default a;
// @Filename: /file2.js
//// import * as foo from './file1';
//// /**/
//// export default foo.b;
goTo.marker("");
verify.completions({
marker: "",
exact: completion.globalsInJsPlus([
"foo",
{
name: "a",
source: "./file1",
sourceDisplay: "./file1",
hasAction: true,
sortText: completion.SortText.AutoImportSuggestions
},
{
name: "b",
source: "./file1",
sourceDisplay: "./file1",
hasAction: true,
sortText: completion.SortText.AutoImportSuggestions
}
]),
preferences: {
includeCompletionsForModuleExports: true,
allowIncompleteCompletions: true,
},
});
@@ -6,6 +6,9 @@
// @Filename: /user2.ts
////unnamed;
// @Filename: /user3.ts
////reExportUnnamed;
// @Filename: /reExportNamed.ts
////export { default } from "./named";
@@ -38,3 +41,9 @@ unnamed;`,
unnamed;`,
]);
goTo.file("/user3.ts");
verify.importFixAtPosition([
`import reExportUnnamed from "./reExportUnnamed";
reExportUnnamed;`]);