diff --git a/core/deno_normalize_import_statement.test.ts b/core/deno_normalize_import_statement.test.ts index e653a4df..7040191d 100644 --- a/core/deno_normalize_import_statement.test.ts +++ b/core/deno_normalize_import_statement.test.ts @@ -60,4 +60,19 @@ test("core / deno_normalize_import_statement", () => { `import { example } from "https://example.com/foo/bar.ts";` ) ).toEqual(`import { example } from "https://example.com/foo/bar.ts";`); + + expect( + normalizeImportStatement(__filename, `import { example } from "./deno";`) + ).toEqual(`import { example } from "./deno.ts";`); + + expect( + normalizeImportStatement( + __filename, + `import { example } from "./testdata/file_walker/a";` + ) + ).toEqual(`import { example } from "./testdata/file_walker/a.js";`); + + expect( + normalizeImportStatement(__filename, `import { example } from "./none";`) + ).toEqual(`import { example } from "./none";`); }); diff --git a/core/deno_normalize_import_statement.ts b/core/deno_normalize_import_statement.ts index 14135b81..e0a2833d 100644 --- a/core/deno_normalize_import_statement.ts +++ b/core/deno_normalize_import_statement.ts @@ -2,13 +2,17 @@ import * as path from "path"; import { getDenoDepsDir } from "./deno"; import { CacheModule } from "./deno_cache"; -import { normalizeFilepath } from "./util"; +import { normalizeFilepath, findNonExtensionModule } from "./util"; import { Logger } from "./logger"; /** * Normalize import statement - * @param importStatement eg. `import { path } from "../../../../Library/Caches/deno/deps/https/example.com/da88efaa8b70cda7903ddc29b8d4c6ea3015de65329ea393289f4104ae2da941"` - * @returns string eg. `https://example.com/demo/sub/mod.ts` + * eg. `import { path } from "../../../../Library/Caches/deno/deps/https/example.com/da88efaa8b70cda7903ddc29b8d4c6ea3015de65329ea393289f4104ae2da941"` + * eg. `import { foo } from "./bar"` + * @param importStatement + * eg. `import { path } from "https://example.com/demo/sub/mod.ts"` + * eg. `import { foo } from "./bar.ts"` + * @returns string */ export function normalizeImportStatement( filename: string, @@ -31,26 +35,28 @@ export function normalizeImportStatement( ? moduleFilepath : path.resolve(path.dirname(filename), moduleFilepath) ); - const rest = matcher[3]; + const rest = matcher[3] || ""; /* istanbul ignore next */ logger?.info( `normalize import \`${importStatement}\` in file \`${filename}\` with module \`${moduleAbsoluteFilepath}\`` ); - /* istanbul ignore else */ if (moduleAbsoluteFilepath.startsWith(getDenoDepsDir())) { const cache = CacheModule.create(moduleAbsoluteFilepath); /* istanbul ignore else */ if (cache) { - importStatement = `import ${importModuleNames} from "${ - cache.meta.url - }"${ - /* istanbul ignore next */ - rest ? rest : "" - }`; + importStatement = `import ${importModuleNames} from "${cache.meta.url}"${rest}`; } } + // if cache not found. then it should be relative path + // eg. `import { foo } from "./bar"` + else if (moduleName.startsWith(".")) { + importStatement = `import ${importModuleNames} from "${findNonExtensionModule( + filename, + moduleName + )}"${rest}`; + } } return importStatement; diff --git a/core/util.test.ts b/core/util.test.ts index 37f5ea48..83b45282 100644 --- a/core/util.test.ts +++ b/core/util.test.ts @@ -7,6 +7,7 @@ import { normalizeFilepath, isValidDenoDocument, isUntitledDocument, + findNonExtensionModule, } from "./util"; test("core / util / pathExists", async () => { @@ -72,3 +73,12 @@ test("core / util / isUntitledDocument", () => { expect(isUntitledDocument("../bar")).toBe(false); expect(isUntitledDocument("untitled: ")).toBe(true); }); + +test("core / util / findNonExtensionModule", () => { + expect(findNonExtensionModule(__filename, "./deno")).toBe("./deno.ts"); + expect(findNonExtensionModule(__filename, "./logger")).toBe("./logger.ts"); + expect(findNonExtensionModule(__filename, "./testdata/file_walker/a")).toBe( + "./testdata/file_walker/a.js" + ); + expect(findNonExtensionModule(__filename, "./none")).toBe("./none"); +}); diff --git a/core/util.ts b/core/util.ts index 45857fa0..ce702883 100644 --- a/core/util.ts +++ b/core/util.ts @@ -1,6 +1,6 @@ import { promises as fs, statSync } from "fs"; import crypto from "crypto"; -import * as path from "path"; +import path from "path"; export function pathExistsSync(filepath: string): boolean { try { @@ -80,3 +80,44 @@ export function isUntitledDocument(filename: string): boolean { // In vscode, tsserver may crash because a temporary document is not saved return /^untitled:/.test(filename); } + +/** + * find module which has no extension name + * We hope it can find the module correctly in Deno's way + * eg. `import { foo } from "./bar"` should be `import { foo } from "./bar.ts"` + * @param filepath + * @param moduleName + */ +export function findNonExtensionModule( + filepath: string, + moduleName: string +): string { + function resolveModule(modulePath: string): string | void { + if (pathExistsSync(path.resolve(path.dirname(filepath), modulePath))) { + return modulePath; + } + + return; + } + + const denoSupportedExtensions = [ + ".ts", + ".tsx", + ".d.ts", + ".js", + ".jsx", + ".mjs", + ]; + + while (denoSupportedExtensions.length) { + const extension = denoSupportedExtensions.shift(); + + const modulePath = resolveModule(moduleName + extension); + + if (modulePath) { + return modulePath; + } + } + + return moduleName; +}