Skip to content

Commit

Permalink
add support of jsx extension
Browse files Browse the repository at this point in the history
  • Loading branch information
Kingwl committed Jun 22, 2018
1 parent 4390a7c commit 04f3e1a
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 23 deletions.
38 changes: 22 additions & 16 deletions src/compiler/moduleSpecifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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];
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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<SourceFile>, getCanonicalFileName: (file: string) => string, host: ModuleSpecifierResolutionHost) {
Expand Down Expand Up @@ -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);
}
});
}
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/services/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down
3 changes: 3 additions & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<SourceFile>, preferences: ModuleSpecifierPreferences): ReadonlyArray<ReadonlyArray<string>>;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion tests/cases/fourslash/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
33 changes: 29 additions & 4 deletions tests/cases/fourslash/importNameCodeFix_jsExtensionPreference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -43,7 +52,7 @@ verify.importFixAtPosition([
`import { a } from "./a.js";
a;`], /* errorCode */ undefined, {
includingJsExtensionOnAutoImports: true
includeExtensionInImports: '.js'
});

goTo.file("/d.ts");
Expand All @@ -57,21 +66,37 @@ verify.importFixAtPosition([
`import { a } from "./a.js";
a;`], /* errorCode */ undefined, {
includingJsExtensionOnAutoImports: true
includeExtensionInImports: '.js'
});

goTo.file("/g.ts");
verify.importFixAtPosition([
`import { f } from "./f.js";
f;`], /* errorCode */ undefined, {
includingJsExtensionOnAutoImports: true
includeExtensionInImports: '.js'
});

goTo.file("/i.ts");
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'
});

0 comments on commit 04f3e1a

Please sign in to comment.