From 19820ca82a039a74835035fb9ea8129b4cae7ace Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 1 Jun 2018 16:23:17 -0700 Subject: [PATCH 1/3] Use more nodelike paths for import types when possible --- src/compiler/checker.ts | 2 +- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 25 ++++++++ ...arationEmitCommonJsModuleReferencedType.js | 38 +++++++++++++ ...onEmitCommonJsModuleReferencedType.symbols | 55 ++++++++++++++++++ ...tionEmitCommonJsModuleReferencedType.types | 57 +++++++++++++++++++ ...mportShouldNotBeElidedInDeclarationEmit.js | 2 +- ...arationEmitCommonJsModuleReferencedType.ts | 23 ++++++++ 8 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.js create mode 100644 tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.symbols create mode 100644 tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.types create mode 100644 tests/cases/compiler/declarationEmitCommonJsModuleReferencedType.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2df1a58387607..b82784de71d50 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4085,7 +4085,7 @@ namespace ts { // ambient module, just use declaration/symbol name (fallthrough) } else { - return `"${getResolvedExternalModuleName(context!.tracker.moduleResolverHost!, file, getSourceFileOfNode(getOriginalNode(context!.enclosingDeclaration)))}"`; + return `"${getResolvedExternalModuleNameForPossiblyExternalModule(context!.tracker.moduleResolverHost!, file, getSourceFileOfNode(getOriginalNode(context!.enclosingDeclaration)))}"`; } } const declaration = symbol.declarations[0]; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3a19762f0a996..40ee1c5c9d8c0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5287,6 +5287,7 @@ namespace ts { getCanonicalFileName(f: string): string; getCommonSourceDirectory(): string; getCurrentDirectory(): string; + getCompilerOptions(): CompilerOptions; } /** @deprecated See comment on SymbolWriter */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 753ab32321de1..21248a1e26805 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2934,6 +2934,31 @@ namespace ts { }; } + /** @internal */ + export function getResolvedExternalModuleNameForPossiblyExternalModule(host: ModuleNameResolverHost, file: SourceFile, referenceFile?: SourceFile) { + const result = getResolvedExternalModuleName(host, file, referenceFile); + const opts = host.getCompilerOptions(); + if (getEmitModuleResolutionKind(opts) === ModuleResolutionKind.NodeJs) { + // Trim leading paths to `node_modules` to allow node module resolution to find the thing in the future without an exact path + // This simplification means if a package.json for `foo` directs us to `foo/lib/main` instead of `foo/index` we'll write `foo/lib/main` over `foo`, however + const parts = getPathComponents(result); + if (parts[0] !== "") { + return result; // rooted path, leave as is + } + else { + parts.shift(); + } + let index = 0; + while (parts[index] && (parts[index] === "." || parts[index] === "..")) { + index++; + } + if (parts[index] && parts[index] === "node_modules") { + return parts.slice(index + 1).join(directorySeparator); + } + } + return result; + } + export function getResolvedExternalModuleName(host: ModuleNameResolverHost, file: SourceFile, referenceFile?: SourceFile): string { return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName); } diff --git a/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.js b/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.js new file mode 100644 index 0000000000000..998eb27133877 --- /dev/null +++ b/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.js @@ -0,0 +1,38 @@ +//// [tests/cases/compiler/declarationEmitCommonJsModuleReferencedType.ts] //// + +//// [index.d.ts] +export interface NestedProps {} +//// [index.d.ts] +export interface OtherIndexProps {} +//// [other.d.ts] +export interface OtherProps {} +//// [index.d.ts] +import { OtherProps } from "./other"; +import { OtherIndexProps } from "./other/index"; +import { NestedProps } from "nested"; +export interface SomeProps {} + +export function foo(): [SomeProps, OtherProps, OtherIndexProps, NestedProps]; +//// [index.d.ts] +export interface RootProps {} + +export function bar(): RootProps; +//// [entry.ts] +import { foo } from "foo"; +import { bar } from "root"; +export const x = foo(); +export const y = bar(); + + +//// [entry.js] +"use strict"; +exports.__esModule = true; +var foo_1 = require("foo"); +var root_1 = require("root"); +exports.x = foo_1.foo(); +exports.y = root_1.bar(); + + +//// [entry.d.ts] +export declare const x: [import("foo/index").SomeProps, import("foo/other").OtherProps, import("foo/other/index").OtherIndexProps, import("foo/node_modules/nested/index").NestedProps]; +export declare const y: import("root/index").RootProps; diff --git a/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.symbols b/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.symbols new file mode 100644 index 0000000000000..c9c0f98baeef2 --- /dev/null +++ b/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.symbols @@ -0,0 +1,55 @@ +=== tests/cases/compiler/r/node_modules/foo/node_modules/nested/index.d.ts === +export interface NestedProps {} +>NestedProps : Symbol(NestedProps, Decl(index.d.ts, 0, 0)) + +=== tests/cases/compiler/r/node_modules/foo/other/index.d.ts === +export interface OtherIndexProps {} +>OtherIndexProps : Symbol(OtherIndexProps, Decl(index.d.ts, 0, 0)) + +=== tests/cases/compiler/r/node_modules/foo/other.d.ts === +export interface OtherProps {} +>OtherProps : Symbol(OtherProps, Decl(other.d.ts, 0, 0)) + +=== tests/cases/compiler/r/node_modules/foo/index.d.ts === +import { OtherProps } from "./other"; +>OtherProps : Symbol(OtherProps, Decl(index.d.ts, 0, 8)) + +import { OtherIndexProps } from "./other/index"; +>OtherIndexProps : Symbol(OtherIndexProps, Decl(index.d.ts, 1, 8)) + +import { NestedProps } from "nested"; +>NestedProps : Symbol(NestedProps, Decl(index.d.ts, 2, 8)) + +export interface SomeProps {} +>SomeProps : Symbol(SomeProps, Decl(index.d.ts, 2, 37)) + +export function foo(): [SomeProps, OtherProps, OtherIndexProps, NestedProps]; +>foo : Symbol(foo, Decl(index.d.ts, 3, 29)) +>SomeProps : Symbol(SomeProps, Decl(index.d.ts, 2, 37)) +>OtherProps : Symbol(OtherProps, Decl(index.d.ts, 0, 8)) +>OtherIndexProps : Symbol(OtherIndexProps, Decl(index.d.ts, 1, 8)) +>NestedProps : Symbol(NestedProps, Decl(index.d.ts, 2, 8)) + +=== tests/cases/compiler/node_modules/root/index.d.ts === +export interface RootProps {} +>RootProps : Symbol(RootProps, Decl(index.d.ts, 0, 0)) + +export function bar(): RootProps; +>bar : Symbol(bar, Decl(index.d.ts, 0, 29)) +>RootProps : Symbol(RootProps, Decl(index.d.ts, 0, 0)) + +=== tests/cases/compiler/r/entry.ts === +import { foo } from "foo"; +>foo : Symbol(foo, Decl(entry.ts, 0, 8)) + +import { bar } from "root"; +>bar : Symbol(bar, Decl(entry.ts, 1, 8)) + +export const x = foo(); +>x : Symbol(x, Decl(entry.ts, 2, 12)) +>foo : Symbol(foo, Decl(entry.ts, 0, 8)) + +export const y = bar(); +>y : Symbol(y, Decl(entry.ts, 3, 12)) +>bar : Symbol(bar, Decl(entry.ts, 1, 8)) + diff --git a/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.types b/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.types new file mode 100644 index 0000000000000..77c252679da0d --- /dev/null +++ b/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.types @@ -0,0 +1,57 @@ +=== tests/cases/compiler/r/node_modules/foo/node_modules/nested/index.d.ts === +export interface NestedProps {} +>NestedProps : NestedProps + +=== tests/cases/compiler/r/node_modules/foo/other/index.d.ts === +export interface OtherIndexProps {} +>OtherIndexProps : OtherIndexProps + +=== tests/cases/compiler/r/node_modules/foo/other.d.ts === +export interface OtherProps {} +>OtherProps : OtherProps + +=== tests/cases/compiler/r/node_modules/foo/index.d.ts === +import { OtherProps } from "./other"; +>OtherProps : any + +import { OtherIndexProps } from "./other/index"; +>OtherIndexProps : any + +import { NestedProps } from "nested"; +>NestedProps : any + +export interface SomeProps {} +>SomeProps : SomeProps + +export function foo(): [SomeProps, OtherProps, OtherIndexProps, NestedProps]; +>foo : () => [SomeProps, OtherProps, OtherIndexProps, NestedProps] +>SomeProps : SomeProps +>OtherProps : OtherProps +>OtherIndexProps : OtherIndexProps +>NestedProps : NestedProps + +=== tests/cases/compiler/node_modules/root/index.d.ts === +export interface RootProps {} +>RootProps : RootProps + +export function bar(): RootProps; +>bar : () => RootProps +>RootProps : RootProps + +=== tests/cases/compiler/r/entry.ts === +import { foo } from "foo"; +>foo : () => [import("tests/cases/compiler/r/node_modules/foo/index").SomeProps, import("tests/cases/compiler/r/node_modules/foo/other").OtherProps, import("tests/cases/compiler/r/node_modules/foo/other/index").OtherIndexProps, import("tests/cases/compiler/r/node_modules/foo/node_modules/nested/index").NestedProps] + +import { bar } from "root"; +>bar : () => import("tests/cases/compiler/node_modules/root/index").RootProps + +export const x = foo(); +>x : [import("tests/cases/compiler/r/node_modules/foo/index").SomeProps, import("tests/cases/compiler/r/node_modules/foo/other").OtherProps, import("tests/cases/compiler/r/node_modules/foo/other/index").OtherIndexProps, import("tests/cases/compiler/r/node_modules/foo/node_modules/nested/index").NestedProps] +>foo() : [import("tests/cases/compiler/r/node_modules/foo/index").SomeProps, import("tests/cases/compiler/r/node_modules/foo/other").OtherProps, import("tests/cases/compiler/r/node_modules/foo/other/index").OtherIndexProps, import("tests/cases/compiler/r/node_modules/foo/node_modules/nested/index").NestedProps] +>foo : () => [import("tests/cases/compiler/r/node_modules/foo/index").SomeProps, import("tests/cases/compiler/r/node_modules/foo/other").OtherProps, import("tests/cases/compiler/r/node_modules/foo/other/index").OtherIndexProps, import("tests/cases/compiler/r/node_modules/foo/node_modules/nested/index").NestedProps] + +export const y = bar(); +>y : import("tests/cases/compiler/node_modules/root/index").RootProps +>bar() : import("tests/cases/compiler/node_modules/root/index").RootProps +>bar : () => import("tests/cases/compiler/node_modules/root/index").RootProps + diff --git a/tests/baselines/reference/importShouldNotBeElidedInDeclarationEmit.js b/tests/baselines/reference/importShouldNotBeElidedInDeclarationEmit.js index bb453e398b28b..65da71910619c 100644 --- a/tests/baselines/reference/importShouldNotBeElidedInDeclarationEmit.js +++ b/tests/baselines/reference/importShouldNotBeElidedInDeclarationEmit.js @@ -21,4 +21,4 @@ exports.thing = umd_1.makeThing(); //// [index.d.ts] -export declare const thing: import("./node_modules/umd").Thing; +export declare const thing: import("umd").Thing; diff --git a/tests/cases/compiler/declarationEmitCommonJsModuleReferencedType.ts b/tests/cases/compiler/declarationEmitCommonJsModuleReferencedType.ts new file mode 100644 index 0000000000000..5207d655037fd --- /dev/null +++ b/tests/cases/compiler/declarationEmitCommonJsModuleReferencedType.ts @@ -0,0 +1,23 @@ +// @declaration: true +// @filename: r/node_modules/foo/node_modules/nested/index.d.ts +export interface NestedProps {} +// @filename: r/node_modules/foo/other/index.d.ts +export interface OtherIndexProps {} +// @filename: r/node_modules/foo/other.d.ts +export interface OtherProps {} +// @filename: r/node_modules/foo/index.d.ts +import { OtherProps } from "./other"; +import { OtherIndexProps } from "./other/index"; +import { NestedProps } from "nested"; +export interface SomeProps {} + +export function foo(): [SomeProps, OtherProps, OtherIndexProps, NestedProps]; +// @filename: node_modules/root/index.d.ts +export interface RootProps {} + +export function bar(): RootProps; +// @filename: r/entry.ts +import { foo } from "foo"; +import { bar } from "root"; +export const x = foo(); +export const y = bar(); From ebfda15799ea2635ab644ec059a33d1e169bdd6a Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 4 Jun 2018 12:29:30 -0700 Subject: [PATCH 2/3] move functionality from services into compiler, fix with propert file/directory conflict handling --- src/compiler/checker.ts | 3 +- src/compiler/moduleNameResolver.ts | 4 -- .../moduleSpecifiers.ts | 39 +++++++++++++------ src/compiler/program.ts | 3 ++ src/compiler/tsconfig.json | 1 + src/compiler/types.ts | 20 ++++++---- src/compiler/utilities.ts | 31 ++------------- src/harness/tsconfig.json | 2 +- src/server/tsconfig.json | 2 +- src/server/tsconfig.library.json | 2 +- src/services/getEditsForFileRename.ts | 2 +- src/services/tsconfig.json | 2 +- .../reference/api/tsserverlibrary.d.ts | 8 ++-- tests/baselines/reference/api/typescript.d.ts | 8 ++-- ...arationEmitCommonJsModuleReferencedType.js | 4 +- 15 files changed, 63 insertions(+), 68 deletions(-) rename src/{services/codefixes => compiler}/moduleSpecifiers.ts (88%) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b82784de71d50..44e7f937a46ec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4085,7 +4085,8 @@ namespace ts { // ambient module, just use declaration/symbol name (fallthrough) } else { - return `"${getResolvedExternalModuleNameForPossiblyExternalModule(context!.tracker.moduleResolverHost!, file, getSourceFileOfNode(getOriginalNode(context!.enclosingDeclaration)))}"`; + const contextFile = getSourceFileOfNode(getOriginalNode(context!.enclosingDeclaration))!; + return `"${file.moduleName || moduleSpecifiers.getModuleSpecifier(compilerOptions, contextFile, contextFile.path, file.path, context!.tracker.moduleResolverHost!)}"`; } } const declaration = symbol.declarations[0]; diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index beaeb7dd05615..f93f2d7eb64d6 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -132,10 +132,6 @@ namespace ts { } } - export interface GetEffectiveTypeRootsHost { - directoryExists?(directoryName: string): boolean; - getCurrentDirectory?(): string; - } export function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined { if (options.typeRoots) { return options.typeRoots; diff --git a/src/services/codefixes/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts similarity index 88% rename from src/services/codefixes/moduleSpecifiers.ts rename to src/compiler/moduleSpecifiers.ts index be92b9172736b..5c427e40d2694 100644 --- a/src/services/codefixes/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -1,10 +1,13 @@ // Used by importFixes to synthesize import module specifiers. /* @internal */ namespace ts.moduleSpecifiers { + export interface ModuleSpecifierPreferences { + importModuleSpecifierPreference?: "relative" | "non-relative"; + } + // Note: fromSourceFile is just for usesJsExtensionOnImports - export function getModuleSpecifier(program: Program, fromSourceFile: SourceFile, fromSourceFileName: string, toFileName: string, host: LanguageServiceHost, preferences: UserPreferences) { - const info = getInfo(program.getCompilerOptions(), fromSourceFile, fromSourceFileName, host); - const compilerOptions = program.getCompilerOptions(); + export function getModuleSpecifier(compilerOptions: CompilerOptions, fromSourceFile: SourceFile, fromSourceFileName: string, toFileName: string, host: ModuleSpecifierResolutionHost, preferences: ModuleSpecifierPreferences = {}) { + const info = getInfo(compilerOptions, fromSourceFile, fromSourceFileName, host); return getGlobalModuleSpecifier(toFileName, info, host, compilerOptions) || first(getLocalModuleSpecifiers(toFileName, info, compilerOptions, preferences)); } @@ -14,15 +17,15 @@ namespace ts.moduleSpecifiers { moduleSymbol: Symbol, program: Program, importingSourceFile: SourceFile, - host: LanguageServiceHost, - preferences: UserPreferences, + host: ModuleSpecifierResolutionHost, + preferences: ModuleSpecifierPreferences, ): ReadonlyArray> { const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol); if (ambient) return [[ambient]]; const compilerOptions = program.getCompilerOptions(); const info = getInfo(compilerOptions, importingSourceFile, importingSourceFile.fileName, host); - const modulePaths = getAllModulePaths(program, moduleSymbol.valueDeclaration.getSourceFile()); + const modulePaths = getAllModulePaths(program, getSourceFileOfNode(moduleSymbol.valueDeclaration)); const global = mapDefined(modulePaths, moduleFileName => getGlobalModuleSpecifier(moduleFileName, info, host, compilerOptions)); return global.length ? global.map(g => [g]) : modulePaths.map(moduleFileName => @@ -36,10 +39,10 @@ namespace ts.moduleSpecifiers { readonly sourceDirectory: string; } // importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path - function getInfo(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: string, host: LanguageServiceHost): Info { + function getInfo(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: string, host: ModuleSpecifierResolutionHost): Info { const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions); const addJsExtension = usesJsExtensionOnImports(importingSourceFile); - const getCanonicalFileName = hostGetCanonicalFileName(host); + const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : true); const sourceDirectory = getDirectoryPath(importingSourceFileName); return { moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory }; } @@ -47,7 +50,7 @@ namespace ts.moduleSpecifiers { function getGlobalModuleSpecifier( moduleFileName: string, { addJsExtension, getCanonicalFileName, sourceDirectory }: Info, - host: LanguageServiceHost, + host: ModuleSpecifierResolutionHost, compilerOptions: CompilerOptions, ) { return tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addJsExtension) @@ -59,7 +62,7 @@ namespace ts.moduleSpecifiers { moduleFileName: string, { moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory }: Info, compilerOptions: CompilerOptions, - preferences: UserPreferences, + preferences: ModuleSpecifierPreferences, ) { const { baseUrl, paths } = compilerOptions; @@ -210,7 +213,7 @@ namespace ts.moduleSpecifiers { function tryGetModuleNameAsNodeModule( options: CompilerOptions, moduleFileName: string, - host: LanguageServiceHost, + host: ModuleSpecifierResolutionHost, getCanonicalFileName: (file: string) => string, sourceDirectory: string, ): string | undefined { @@ -256,7 +259,8 @@ namespace ts.moduleSpecifiers { const fullModulePathWithoutExtension = removeFileExtension(path); // If the file is /index, it can be imported by its directory name - if (getCanonicalFileName(fullModulePathWithoutExtension.substring(parts.fileNameIndex)) === "/index") { + // IFF there is not _also_ a file by the same name + if (getCanonicalFileName(fullModulePathWithoutExtension.substring(parts.fileNameIndex)) === "/index" && !tryGetAnyFileFromPath(host, fullModulePathWithoutExtension.substring(0, parts.fileNameIndex))) { return fullModulePathWithoutExtension.substring(0, parts.fileNameIndex); } @@ -264,6 +268,17 @@ namespace ts.moduleSpecifiers { } } + function tryGetAnyFileFromPath(host: ModuleSpecifierResolutionHost, path: string) { + // We check all js, `node` and `json` extensions in addition to TS, since node module resolution would also choose those over the directory + const extensions = getSupportedExtensions({ allowJs: true }, [{ extension: "node", isMixedContent: false }, { extension: "json", isMixedContent: false, scriptKind: ScriptKind.JSON }]); + for (const e of extensions) { + const fullPath = path + e; + if (host.fileExists!(fullPath)) { + return fullPath; + } + } + } + interface NodeModulePathParts { readonly topLevelNodeModulesIndex: number; readonly topLevelPackageNameIndex: number; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 715f5377b1692..e93dada9eda7d 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1169,6 +1169,9 @@ namespace ts { writeFile: writeFileCallback || ( (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)), isEmitBlocked, + readFile: f => host.readFile(f), + fileExists: f => host.fileExists(f), + ...(host.directoryExists ? { directoryExists: f => host.directoryExists!(f) } : {}), }; } diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index 46e5384434a01..5716a2a417d56 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -44,6 +44,7 @@ "builderState.ts", "builder.ts", "resolutionCache.ts", + "moduleSpecifiers.ts", "watch.ts", "commandLineParser.ts", "tsc.ts" diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 40ee1c5c9d8c0..81c89b1580f5a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5019,8 +5019,9 @@ namespace ts { } /* @internal */ - export interface EmitHost extends ScriptReferenceHost { + export interface EmitHost extends ScriptReferenceHost, ModuleSpecifierResolutionHost { getSourceFiles(): ReadonlyArray; + getCurrentDirectory(): string; /* @internal */ isSourceFileFromExternalLibrary(file: SourceFile): boolean; @@ -5282,12 +5283,15 @@ namespace ts { isAtStartOfLine(): boolean; } - /* @internal */ - export interface ModuleNameResolverHost { - getCanonicalFileName(f: string): string; - getCommonSourceDirectory(): string; - getCurrentDirectory(): string; - getCompilerOptions(): CompilerOptions; + export interface GetEffectiveTypeRootsHost { + directoryExists?(directoryName: string): boolean; + getCurrentDirectory?(): string; + } + /** @internal */ + export interface ModuleSpecifierResolutionHost extends GetEffectiveTypeRootsHost { + useCaseSensitiveFileNames?(): boolean; + fileExists?(path: string): boolean; + readFile?(path: string): string | undefined; } /** @deprecated See comment on SymbolWriter */ @@ -5301,7 +5305,7 @@ namespace ts { reportPrivateInBaseOfClassExpression?(propertyName: string): void; reportInaccessibleUniqueSymbolError?(): void; /* @internal */ - moduleResolverHost?: ModuleNameResolverHost; + moduleResolverHost?: ModuleSpecifierResolutionHost; /* @internal */ trackReferencedAmbientModule?(decl: ModuleDeclaration): void; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 21248a1e26805..dc5ef95fa817a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2934,36 +2934,11 @@ namespace ts { }; } - /** @internal */ - export function getResolvedExternalModuleNameForPossiblyExternalModule(host: ModuleNameResolverHost, file: SourceFile, referenceFile?: SourceFile) { - const result = getResolvedExternalModuleName(host, file, referenceFile); - const opts = host.getCompilerOptions(); - if (getEmitModuleResolutionKind(opts) === ModuleResolutionKind.NodeJs) { - // Trim leading paths to `node_modules` to allow node module resolution to find the thing in the future without an exact path - // This simplification means if a package.json for `foo` directs us to `foo/lib/main` instead of `foo/index` we'll write `foo/lib/main` over `foo`, however - const parts = getPathComponents(result); - if (parts[0] !== "") { - return result; // rooted path, leave as is - } - else { - parts.shift(); - } - let index = 0; - while (parts[index] && (parts[index] === "." || parts[index] === "..")) { - index++; - } - if (parts[index] && parts[index] === "node_modules") { - return parts.slice(index + 1).join(directorySeparator); - } - } - return result; - } - - export function getResolvedExternalModuleName(host: ModuleNameResolverHost, file: SourceFile, referenceFile?: SourceFile): string { + export function getResolvedExternalModuleName(host: EmitHost, file: SourceFile, referenceFile?: SourceFile): string { return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName); } - export function getExternalModuleNameFromDeclaration(host: ModuleNameResolverHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string | undefined { + export function getExternalModuleNameFromDeclaration(host: EmitHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string | undefined { const file = resolver.getExternalModuleFileFromDeclaration(declaration); if (!file || file.isDeclarationFile) { return undefined; @@ -2974,7 +2949,7 @@ namespace ts { /** * Resolves a local path to a path which is absolute to the base of the emit */ - export function getExternalModuleNameFromPath(host: ModuleNameResolverHost, fileName: string, referencePath?: string): string { + export function getExternalModuleNameFromPath(host: EmitHost, fileName: string, referencePath?: string): string { const getCanonicalFileName = (f: string) => host.getCanonicalFileName(f); const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName); const filePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory()); diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index ae409e96b8770..c401b40a339b5 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -51,6 +51,7 @@ "../compiler/builderState.ts", "../compiler/builder.ts", "../compiler/resolutionCache.ts", + "../compiler/moduleSpecifiers.ts", "../compiler/watch.ts", "../compiler/commandLineParser.ts", @@ -115,7 +116,6 @@ "../services/codefixes/inferFromUsage.ts", "../services/codefixes/fixInvalidImportSyntax.ts", "../services/codefixes/fixStrictClassInitialization.ts", - "../services/codefixes/moduleSpecifiers.ts", "../services/codefixes/requireInTs.ts", "../services/codefixes/useDefaultImport.ts", "../services/codefixes/fixAddModuleReferTypeMissingTypeof.ts", diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json index a92a19f9c018a..cb0485321ca51 100644 --- a/src/server/tsconfig.json +++ b/src/server/tsconfig.json @@ -47,6 +47,7 @@ "../compiler/builderState.ts", "../compiler/builder.ts", "../compiler/resolutionCache.ts", + "../compiler/moduleSpecifiers.ts", "../compiler/watch.ts", "../compiler/commandLineParser.ts", @@ -111,7 +112,6 @@ "../services/codefixes/inferFromUsage.ts", "../services/codefixes/fixInvalidImportSyntax.ts", "../services/codefixes/fixStrictClassInitialization.ts", - "../services/codefixes/moduleSpecifiers.ts", "../services/codefixes/requireInTs.ts", "../services/codefixes/useDefaultImport.ts", "../services/codefixes/fixAddModuleReferTypeMissingTypeof.ts", diff --git a/src/server/tsconfig.library.json b/src/server/tsconfig.library.json index 1675002c653ad..1adfe2a4bd043 100644 --- a/src/server/tsconfig.library.json +++ b/src/server/tsconfig.library.json @@ -53,6 +53,7 @@ "../compiler/builderState.ts", "../compiler/builder.ts", "../compiler/resolutionCache.ts", + "../compiler/moduleSpecifiers.ts", "../compiler/watch.ts", "../compiler/commandLineParser.ts", @@ -117,7 +118,6 @@ "../services/codefixes/inferFromUsage.ts", "../services/codefixes/fixInvalidImportSyntax.ts", "../services/codefixes/fixStrictClassInitialization.ts", - "../services/codefixes/moduleSpecifiers.ts", "../services/codefixes/requireInTs.ts", "../services/codefixes/useDefaultImport.ts", "../services/codefixes/fixAddModuleReferTypeMissingTypeof.ts", diff --git a/src/services/getEditsForFileRename.ts b/src/services/getEditsForFileRename.ts index 4bac4a2516dc9..a7e41d2014f31 100644 --- a/src/services/getEditsForFileRename.ts +++ b/src/services/getEditsForFileRename.ts @@ -123,7 +123,7 @@ namespace ts { // TODO:GH#18217 ? getSourceFileToImportFromResolved(resolveModuleName(importLiteral.text, oldImportFromPath, program.getCompilerOptions(), host as ModuleResolutionHost), oldToNew, program) : getSourceFileToImport(importLiteral, sourceFile, program, host, oldToNew); - return toImport === undefined ? undefined : moduleSpecifiers.getModuleSpecifier(program, sourceFile, newImportFromPath, toImport, host, preferences); + return toImport === undefined ? undefined : moduleSpecifiers.getModuleSpecifier(program.getCompilerOptions(), sourceFile, newImportFromPath, toImport, host, preferences); }); } } diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 7d6687fb987e9..237142fc5bff6 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -44,6 +44,7 @@ "../compiler/builderState.ts", "../compiler/builder.ts", "../compiler/resolutionCache.ts", + "../compiler/moduleSpecifiers.ts", "../compiler/watch.ts", "../compiler/commandLineParser.ts", @@ -108,7 +109,6 @@ "codefixes/inferFromUsage.ts", "codefixes/fixInvalidImportSyntax.ts", "codefixes/fixStrictClassInitialization.ts", - "codefixes/moduleSpecifiers.ts", "codefixes/requireInTs.ts", "codefixes/useDefaultImport.ts", "codefixes/fixAddModuleReferTypeMissingTypeof.ts", diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 8f065e362d2e8..d4f185e1fefbb 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2876,6 +2876,10 @@ declare namespace ts { omitTrailingSemicolon?: boolean; noEmitHelpers?: boolean; } + interface GetEffectiveTypeRootsHost { + directoryExists?(directoryName: string): boolean; + getCurrentDirectory?(): string; + } /** @deprecated See comment on SymbolWriter */ interface SymbolTracker { trackSymbol?(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void; @@ -3467,10 +3471,6 @@ declare namespace ts { function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile; } declare namespace ts { - interface GetEffectiveTypeRootsHost { - directoryExists?(directoryName: string): boolean; - getCurrentDirectory?(): string; - } function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined; /** * @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown. diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 86c001cdec450..2660d26cba500 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2876,6 +2876,10 @@ declare namespace ts { omitTrailingSemicolon?: boolean; noEmitHelpers?: boolean; } + interface GetEffectiveTypeRootsHost { + directoryExists?(directoryName: string): boolean; + getCurrentDirectory?(): string; + } /** @deprecated See comment on SymbolWriter */ interface SymbolTracker { trackSymbol?(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void; @@ -3467,10 +3471,6 @@ declare namespace ts { function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile; } declare namespace ts { - interface GetEffectiveTypeRootsHost { - directoryExists?(directoryName: string): boolean; - getCurrentDirectory?(): string; - } function getEffectiveTypeRoots(options: CompilerOptions, host: GetEffectiveTypeRootsHost): string[] | undefined; /** * @param {string | undefined} containingFile - file that contains type reference directive, can be undefined if containing file is unknown. diff --git a/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.js b/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.js index 998eb27133877..1e32c5c4c262b 100644 --- a/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.js +++ b/tests/baselines/reference/declarationEmitCommonJsModuleReferencedType.js @@ -34,5 +34,5 @@ exports.y = root_1.bar(); //// [entry.d.ts] -export declare const x: [import("foo/index").SomeProps, import("foo/other").OtherProps, import("foo/other/index").OtherIndexProps, import("foo/node_modules/nested/index").NestedProps]; -export declare const y: import("root/index").RootProps; +export declare const x: [import("foo").SomeProps, import("foo/other").OtherProps, import("foo/other/index").OtherIndexProps, import("foo/node_modules/nested").NestedProps]; +export declare const y: import("root").RootProps; From 5eb65a06a53851fb994c66ba2c8a065e30b93d82 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 5 Jun 2018 12:36:56 -0700 Subject: [PATCH 3/3] mark suspect cast --- src/compiler/moduleSpecifiers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 5c427e40d2694..50daa81d60c3f 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -273,7 +273,7 @@ namespace ts.moduleSpecifiers { const extensions = getSupportedExtensions({ allowJs: true }, [{ extension: "node", isMixedContent: false }, { extension: "json", isMixedContent: false, scriptKind: ScriptKind.JSON }]); for (const e of extensions) { const fullPath = path + e; - if (host.fileExists!(fullPath)) { + if (host.fileExists!(fullPath)) { // TODO: GH#18217 return fullPath; } }