From 04f3e1a843dbe0aaff869587f9f12fe7ffcb3d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Sat, 23 Jun 2018 01:07:40 +0800 Subject: [PATCH] add support of jsx extension --- src/compiler/moduleSpecifiers.ts | 38 +++++++++++-------- src/server/protocol.ts | 2 +- src/services/types.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 3 ++ tests/baselines/reference/api/typescript.d.ts | 1 + tests/cases/fourslash/fourslash.ts | 2 +- ...importNameCodeFix_jsExtensionPreference.ts | 33 ++++++++++++++-- 7 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 59745f82340bb..27152627dd2e4 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -3,7 +3,7 @@ namespace ts.moduleSpecifiers { export interface ModuleSpecifierPreferences { importModuleSpecifierPreference?: "relative" | "non-relative"; - includingJsExtensionOnAutoImports?: boolean; + includeExtensionInImports?: Extension.Js | Extension.Jsx; } // Note: fromSourceFile is just for usesJsExtensionOnImports @@ -38,39 +38,39 @@ namespace ts.moduleSpecifiers { interface Info { readonly moduleResolutionKind: ModuleResolutionKind; - readonly addJsExtension: boolean; + readonly addExtension: Extension.Js | Extension.Jsx | undefined; readonly getCanonicalFileName: GetCanonicalFileName; readonly sourceDirectory: string; } // importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path function getInfo(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: string, host: ModuleSpecifierResolutionHost, preferences: ModuleSpecifierPreferences): Info { const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions); - const addJsExtension = usesJsExtensionOnImports(importingSourceFile, preferences); + const addExtension = usesExtensionOnImports(importingSourceFile, preferences); const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : true); const sourceDirectory = getDirectoryPath(importingSourceFileName); - return { moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory }; + return { moduleResolutionKind, addExtension, getCanonicalFileName, sourceDirectory }; } function getGlobalModuleSpecifier( moduleFileName: string, - { addJsExtension, getCanonicalFileName, sourceDirectory }: Info, + { addExtension, getCanonicalFileName, sourceDirectory }: Info, host: ModuleSpecifierResolutionHost, compilerOptions: CompilerOptions, ) { - return tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addJsExtension) + return tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addExtension) || tryGetModuleNameAsNodeModule(compilerOptions, moduleFileName, host, getCanonicalFileName, sourceDirectory) || compilerOptions.rootDirs && tryGetModuleNameFromRootDirs(compilerOptions.rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName); } function getLocalModuleSpecifiers( moduleFileName: string, - { moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory }: Info, + { moduleResolutionKind, addExtension, getCanonicalFileName, sourceDirectory }: Info, compilerOptions: CompilerOptions, preferences: ModuleSpecifierPreferences, ) { const { baseUrl, paths } = compilerOptions; - const relativePath = removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), moduleResolutionKind, addJsExtension); + const relativePath = removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), moduleResolutionKind, addExtension); if (!baseUrl || preferences.importModuleSpecifierPreference === "relative") { return [relativePath]; } @@ -80,7 +80,7 @@ namespace ts.moduleSpecifiers { return [relativePath]; } - const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, moduleResolutionKind, addJsExtension); + const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, moduleResolutionKind, addExtension); if (paths) { const fromPaths = tryGetModuleNameFromPaths(removeFileExtension(relativeToBaseUrl), importRelativeToBaseUrl, paths); if (fromPaths) { @@ -130,8 +130,14 @@ namespace ts.moduleSpecifiers { return relativeFirst ? [relativePath, importRelativeToBaseUrl] : [importRelativeToBaseUrl, relativePath]; } - function usesJsExtensionOnImports({ imports }: SourceFile, preferences: ModuleSpecifierPreferences): boolean { - return firstDefined(imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || !!preferences.includingJsExtensionOnAutoImports; + function tryGetFileExtension (text: string) { + return pathIsRelative(text) && fileExtensionIsOneOf(text, [Extension.Js, Extension.Jsx]) ? ( + fileExtensionIs(text, Extension.Js) ? Extension.Js : Extension.Jsx + ) : undefined; + } + + function usesExtensionOnImports({ imports }: SourceFile, preferences: ModuleSpecifierPreferences): Extension.Js | Extension.Jsx | undefined { + return preferences.includeExtensionInImports === undefined ? firstDefined(imports, ({ text }) => tryGetFileExtension(text)) : preferences.includeExtensionInImports; } function discoverProbableSymlinks(files: ReadonlyArray, getCanonicalFileName: (file: string) => string, host: ModuleSpecifierResolutionHost) { @@ -245,14 +251,14 @@ namespace ts.moduleSpecifiers { host: GetEffectiveTypeRootsHost, getCanonicalFileName: (file: string) => string, moduleFileName: string, - addJsExtension: boolean, + addExtension: Extension.Js | Extension.Jsx | undefined, ): string | undefined { const roots = getEffectiveTypeRoots(options, host); return firstDefined(roots, unNormalizedTypeRoot => { const typeRoot = toPath(unNormalizedTypeRoot, /*basePath*/ undefined, getCanonicalFileName); if (startsWith(moduleFileName, typeRoot)) { // For a type definition, we can strip `/index` even with classic resolution. - return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), ModuleResolutionKind.NodeJs, addJsExtension); + return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), ModuleResolutionKind.NodeJs, addExtension); } }); } @@ -397,10 +403,10 @@ namespace ts.moduleSpecifiers { }); } - function removeExtensionAndIndexPostFix(fileName: string, moduleResolutionKind: ModuleResolutionKind, addJsExtension: boolean): string { + function removeExtensionAndIndexPostFix(fileName: string, moduleResolutionKind: ModuleResolutionKind, addExtension: Extension.Js | Extension.Jsx | undefined): string { const noExtension = removeFileExtension(fileName); - return addJsExtension - ? noExtension + ".js" + return addExtension + ? noExtension + addExtension : moduleResolutionKind === ModuleResolutionKind.NodeJs ? removeSuffix(noExtension, "/index") : noExtension; diff --git a/src/server/protocol.ts b/src/server/protocol.ts index b051cd6e65258..677f5b0d29326 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2719,7 +2719,7 @@ namespace ts.server.protocol { readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; readonly allowTextChangesInNewFiles?: boolean; - readonly includingJsExtensionOnAutoImports?: boolean; + readonly includeExtensionInImports?: Extension.Js | Extension.Jsx; } export interface CompilerOptions { diff --git a/src/services/types.ts b/src/services/types.ts index d3f87e8e61ae8..f0b5f7f19b824 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -240,7 +240,7 @@ namespace ts { readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; readonly allowTextChangesInNewFiles?: boolean; - readonly includingJsExtensionOnAutoImports?: boolean; + readonly includeExtensionInImports?: Extension.Js | Extension.Jsx; } /* @internal */ export const defaultPreferences: UserPreferences = {}; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index a9fe9a32556dc..1f5fcd651330f 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -9234,6 +9234,7 @@ declare namespace ts { declare namespace ts.moduleSpecifiers { interface ModuleSpecifierPreferences { importModuleSpecifierPreference?: "relative" | "non-relative"; + includeExtensionInImports?: Extension.Js | Extension.Jsx; } function getModuleSpecifier(compilerOptions: CompilerOptions, fromSourceFile: SourceFile, fromSourceFileName: string, toFileName: string, host: ModuleSpecifierResolutionHost, preferences?: ModuleSpecifierPreferences): string; function getModuleSpecifiers(moduleSymbol: Symbol, compilerOptions: CompilerOptions, importingSourceFile: SourceFile, host: ModuleSpecifierResolutionHost, files: ReadonlyArray, preferences: ModuleSpecifierPreferences): ReadonlyArray>; @@ -9925,6 +9926,7 @@ declare namespace ts { readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; readonly allowTextChangesInNewFiles?: boolean; + readonly includeExtensionInImports?: Extension.Js | Extension.Jsx; } const defaultPreferences: UserPreferences; interface LanguageService { @@ -13176,6 +13178,7 @@ declare namespace ts.server.protocol { readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; readonly allowTextChangesInNewFiles?: boolean; + readonly includeExtensionInImports?: Extension.Js | Extension.Jsx; } interface CompilerOptions { allowJs?: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 8bb1efe058b12..c4ebc9c4cb20a 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4717,6 +4717,7 @@ declare namespace ts { readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; readonly allowTextChangesInNewFiles?: boolean; + readonly includeExtensionInImports?: Extension.Js | Extension.Jsx; } interface LanguageService { cleanupSemanticCache(): void; diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index f75f84b980408..2246efc026638 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -530,7 +530,7 @@ declare namespace FourSlashInterface { includeCompletionsForModuleExports?: boolean; includeInsertTextCompletions?: boolean; importModuleSpecifierPreference?: "relative" | "non-relative"; - includingJsExtensionOnAutoImports?: boolean + includeExtensionInImports?: '.js' | '.jsx' } interface CompletionsAtOptions extends UserPreferences { triggerCharacter?: string; diff --git a/tests/cases/fourslash/importNameCodeFix_jsExtensionPreference.ts b/tests/cases/fourslash/importNameCodeFix_jsExtensionPreference.ts index 25f093c8ac247..344e594095911 100644 --- a/tests/cases/fourslash/importNameCodeFix_jsExtensionPreference.ts +++ b/tests/cases/fourslash/importNameCodeFix_jsExtensionPreference.ts @@ -32,6 +32,15 @@ // @Filename: /i.ts ////h; +// @Filename: /j.jsx +////export function j() {} + +// @Filename: /k.js +////j; + +// @Filename: /l.js +////j; + goTo.file("/b.js"); verify.importFixAtPosition([ `import { a } from "./a"; @@ -43,7 +52,7 @@ verify.importFixAtPosition([ `import { a } from "./a.js"; a;`], /* errorCode */ undefined, { - includingJsExtensionOnAutoImports: true + includeExtensionInImports: '.js' }); goTo.file("/d.ts"); @@ -57,7 +66,7 @@ verify.importFixAtPosition([ `import { a } from "./a.js"; a;`], /* errorCode */ undefined, { - includingJsExtensionOnAutoImports: true + includeExtensionInImports: '.js' }); goTo.file("/g.ts"); @@ -65,7 +74,7 @@ verify.importFixAtPosition([ `import { f } from "./f.js"; f;`], /* errorCode */ undefined, { - includingJsExtensionOnAutoImports: true + includeExtensionInImports: '.js' }); goTo.file("/i.ts"); @@ -73,5 +82,21 @@ verify.importFixAtPosition([ `import h from "./h.js"; h;`], /* errorCode */ undefined, { - includingJsExtensionOnAutoImports: true + includeExtensionInImports: '.js' +}); + +goTo.file("/k.js"); +verify.importFixAtPosition([ +`import { j } from "./j.js"; + +j;`], /* errorCode */ undefined, { + includeExtensionInImports: '.js' +}); + +goTo.file("/l.js"); +verify.importFixAtPosition([ +`import { j } from "./j.jsx"; + +j;`], /* errorCode */ undefined, { + includeExtensionInImports: '.jsx' }); \ No newline at end of file