From 049a5fba0711feace47ec024fd6f6923369013d3 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 18 Aug 2015 13:36:08 -0700 Subject: [PATCH] added tests --- src/compiler/program.ts | 42 ++++---- tests/cases/unittests/moduleResolution.ts | 117 ++++++++++++++-------- 2 files changed, 98 insertions(+), 61 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 30e1c315eb1..62038aa3c55 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -37,13 +37,13 @@ namespace ts { export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModule { switch(compilerOptions.moduleResolution) { - case ModuleResolutionKind.NodeJs: return nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host); - case ModuleResolutionKind.BaseUrl: return baseUrlModuleNameResolver(moduleName, containingFile, compilerOptions, host); + case ModuleResolutionKind.NodeJs: return nodeModuleNameResolver(moduleName, containingFile, host); + case ModuleResolutionKind.BaseUrl: return baseUrlModuleNameResolver(moduleName, containingFile, compilerOptions.baseUrl, host); default: return legacyNameResolver(moduleName, containingFile, compilerOptions, host); } } - export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModule { + export function nodeModuleNameResolver(moduleName: string, containingFile: string, host: ModuleResolutionHost): ResolvedModule { let containingDirectory = getDirectoryPath(containingFile); if (getRootLength(moduleName) !== 0 || nameStartsWithDotSlashOrDotDotSlash(moduleName)) { @@ -132,30 +132,34 @@ namespace ts { return { resolvedFileName: undefined, failedLookupLocations }; } - export function baseUrlModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModule { - Debug.assert(compilerOptions.baseUrl !== undefined, "baseUrl is mandatory when using this module resolution strategy"); + export function baseUrlModuleNameResolver(moduleName: string, containingFile: string, baseUrl: string, host: ModuleResolutionHost): ResolvedModule { + Debug.assert(baseUrl !== undefined); - let normalizedModuleName = normalizeSlashes(moduleName); - - // treat module name as url that is relative to containing file if - let basePart = useBaseUrl(moduleName) ? compilerOptions.baseUrl : getDirectoryPath(containingFile); + let normalizedModuleName = normalizeSlashes(moduleName); + let basePart = useBaseUrl(moduleName) ? baseUrl : getDirectoryPath(containingFile); let candidate = normalizePath(combinePaths(basePart, moduleName)); - let failedLookupLocations: string[] = []; - // first - try to load file as is - let result = tryLoadFile(candidate); - if (result) { - return result; - } - // then try all supported extension - for(let ext of supportedExtensions) { - let result = tryLoadFile(candidate + ext); + + let hasSupportedExtension = forEach(supportedExtensions, ext => fileExtensionIs(candidate, ext)); + + if (hasSupportedExtension) { + // module name already has extension - use it as is + let result = tryLoadFile(candidate); if (result) { return result; + } + } + else { + // module name does not have extension - try every supported extension + for(let ext of supportedExtensions) { + let result = tryLoadFile(candidate + ext); + if (result) { + return result; + } } } - + return { resolvedFileName: undefined, failedLookupLocations }; function tryLoadFile(location: string): ResolvedModule { diff --git a/tests/cases/unittests/moduleResolution.ts b/tests/cases/unittests/moduleResolution.ts index 7ae2cbe6ec5..d0c3c4fdc7d 100644 --- a/tests/cases/unittests/moduleResolution.ts +++ b/tests/cases/unittests/moduleResolution.ts @@ -44,42 +44,38 @@ module ts { return !path && fse; } } - } - - function isDirectory(fse: Directory | File): boolean { - return (fse).children !== undefined; - } - - function createDirectory(name: string): Directory { - return { name, children: {} } - } - - function makeFS(files: File[]): Directory { - // create root - let {dir} = splitPath(files[0].name); - let root: Directory = createDirectory(dir); - for(let f of files) { - addFile(f.name, f.content, root); + function isDirectory(fse: Directory | File): boolean { + return (fse).children !== undefined; } - function addFile(path: string, content: string, parent: Directory) { - Debug.assert(parent !== undefined); + function makeFS(files: File[]): Directory { + // create root + let {dir} = splitPath(files[0].name); + let root: Directory = { name: dir, children: {} }; - let {dir, rel} = splitPath(path); - if (rel) { - let d = parent.children[dir] || (parent.children[dir] = createDirectory(dir)); - Debug.assert(isDirectory(d)) - addFile(rel, content, d); + for(let f of files) { + addFile(f.name, f.content, root); } - else { - parent.children[dir] = { name: dir, content }; + + function addFile(path: string, content: string, parent: Directory) { + Debug.assert(parent !== undefined); + + let {dir, rel} = splitPath(path); + if (rel) { + let d = parent.children[dir] || (parent.children[dir] = { name: dir, children: {} }); + Debug.assert(isDirectory(d)) + addFile(rel, content, d); + } + else { + parent.children[dir] = { name: dir, content }; + } } + + return root; } - - return root; } - + function splitPath(path: string): { dir: string; rel: string } { let index = path.indexOf(directorySeparator); return index === -1 @@ -87,8 +83,6 @@ module ts { : { dir: path.substr(0, index), rel: path.substr(index + 1) }; } - let opts: CompilerOptions = { moduleResolution: ModuleResolutionKind.NodeJs }; - describe("Node module resolution - relative paths", () => { function testLoadAsFile(containingFileName: string, moduleFileNameNoExt: string, moduleName: string): void { @@ -97,7 +91,7 @@ module ts { let containingFile = { name: containingFileName, content: ""} let moduleFile = { name: moduleFileNameNoExt + ".d.ts", content: "var x;"} - let resolution = nodeModuleNameResolver(moduleName, containingFile.name, opts, createModuleResolutionHost(containingFile, moduleFile)); + let resolution = nodeModuleNameResolver(moduleName, containingFile.name, createModuleResolutionHost(containingFile, moduleFile)); assert.equal(resolution.resolvedFileName, moduleFile.name); assert.isTrue(resolution.failedLookupLocations.length === 0); @@ -107,7 +101,7 @@ module ts { let containingFile = { name: containingFileName, content: ""} let moduleFile = { name: moduleFileNameNoExt + ".ts", content: "var x;"} - let resolution = nodeModuleNameResolver(moduleName, containingFile.name, opts, createModuleResolutionHost(containingFile, moduleFile)); + let resolution = nodeModuleNameResolver(moduleName, containingFile.name, createModuleResolutionHost(containingFile, moduleFile)); assert.equal(resolution.resolvedFileName, undefined); assert.equal(resolution.failedLookupLocations.length, 3); @@ -130,12 +124,16 @@ module ts { it("module name that starts with '/' script extension resolved as relative file name", () => { testLoadAsFile("/foo/bar/baz.ts", "/foo", "/foo"); }); + + it("module name that starts with 'c:/' script extension resolved as relative file name", () => { + testLoadAsFile("c:/foo/bar/baz.ts", "c:/foo", "c:/foo"); + }); function testLoadingFromPackageJson(containingFileName: string, packageJsonFileName: string, fieldName: string, fieldRef: string, moduleFileName: string, moduleName: string): void { let containingFile = { name: containingFileName }; let packageJson = { name: packageJsonFileName, content: JSON.stringify({ [fieldName]: fieldRef }) }; let moduleFile = { name: moduleFileName }; - let resolution = nodeModuleNameResolver(moduleName, containingFile.name, opts, createModuleResolutionHost(containingFile, packageJson, moduleFile)); + let resolution = nodeModuleNameResolver(moduleName, containingFile.name, createModuleResolutionHost(containingFile, packageJson, moduleFile)); assert.equal(resolution.resolvedFileName, moduleFile.name); // expect one failed lookup location - attempt to load module as file assert.equal(resolution.failedLookupLocations.length, 1); @@ -145,19 +143,21 @@ module ts { testLoadingFromPackageJson("/a/b/c/d.ts", "/a/b/c/bar/package.json", "typings", "c/d/e.d.ts", "/a/b/c/bar/c/d/e.d.ts", "./bar"); testLoadingFromPackageJson("/a/b/c/d.ts", "/a/bar/package.json", "typings", "e.d.ts", "/a/bar/e.d.ts", "../../bar"); testLoadingFromPackageJson("/a/b/c/d.ts", "/bar/package.json", "typings", "e.d.ts", "/bar/e.d.ts", "/bar"); + testLoadingFromPackageJson("c:/a/b/c/d.ts", "c:/bar/package.json", "typings", "e.d.ts", "c:/bar/e.d.ts", "c:/bar"); }); it("module name as directory - load from main", () => { testLoadingFromPackageJson("/a/b/c/d.ts", "/a/b/c/bar/package.json", "main", "c/d/e.d.ts", "/a/b/c/bar/c/d/e.d.ts", "./bar"); testLoadingFromPackageJson("/a/b/c/d.ts", "/a/bar/package.json", "main", "e.d.ts", "/a/bar/e.d.ts", "../../bar"); testLoadingFromPackageJson("/a/b/c/d.ts", "/bar/package.json", "main", "e.d.ts", "/bar/e.d.ts", "/bar"); + testLoadingFromPackageJson("c:/a/b/c/d.ts", "c:/bar/package.json", "main", "e.d.ts", "c:/bar/e.d.ts", "c:/bar"); }); it ("module name as directory - load index.d.ts", () => { let containingFile = {name: "/a/b/c.ts"}; let packageJson = {name: "/a/b/foo/package.json", content: JSON.stringify({main: "/c/d"})}; let indexFile = { name: "/a/b/foo/index.d.ts" }; - let resolution = nodeModuleNameResolver("./foo", containingFile.name, opts, createModuleResolutionHost(containingFile, packageJson, indexFile)); + let resolution = nodeModuleNameResolver("./foo", containingFile.name, createModuleResolutionHost(containingFile, packageJson, indexFile)); assert.equal(resolution.resolvedFileName, indexFile.name); // expect 2 failed lookup locations: assert.deepEqual(resolution.failedLookupLocations, [ @@ -171,7 +171,7 @@ module ts { it("load module as file - ts files not loaded", () => { let containingFile = { name: "/a/b/c/d/e.ts" }; let moduleFile = { name: "/a/b/node_modules/foo.ts" }; - let resolution = nodeModuleNameResolver("foo", containingFile.name, opts, createModuleResolutionHost(containingFile, moduleFile)); + let resolution = nodeModuleNameResolver("foo", containingFile.name, createModuleResolutionHost(containingFile, moduleFile)); assert.equal(resolution.resolvedFileName, undefined); assert.deepEqual(resolution.failedLookupLocations, [ "/a/b/c/d/node_modules/foo.d.ts", @@ -195,14 +195,14 @@ module ts { it("load module as file", () => { let containingFile = { name: "/a/b/c/d/e.ts" }; let moduleFile = { name: "/a/b/node_modules/foo.d.ts" }; - let resolution = nodeModuleNameResolver("foo", containingFile.name, opts, createModuleResolutionHost(containingFile, moduleFile)); + let resolution = nodeModuleNameResolver("foo", containingFile.name, createModuleResolutionHost(containingFile, moduleFile)); assert.equal(resolution.resolvedFileName, moduleFile.name); }); it("load module as directory", () => { let containingFile = { name: "/a/node_modules/b/c/node_modules/d/e.ts" }; let moduleFile = { name: "/a/node_modules/foo/index.d.ts" }; - let resolution = nodeModuleNameResolver("foo", containingFile.name, opts, createModuleResolutionHost(containingFile, moduleFile)); + let resolution = nodeModuleNameResolver("foo", containingFile.name, createModuleResolutionHost(containingFile, moduleFile)); assert.equal(resolution.resolvedFileName, moduleFile.name); assert.deepEqual(resolution.failedLookupLocations, [ "/a/node_modules/b/c/node_modules/d/node_modules/foo.d.ts", @@ -221,16 +221,36 @@ module ts { }); describe("BaseUrl mode", () => { - function getCompilerOptions(baseUrl: string): CompilerOptions { - return { baseUrl, moduleResolution: ModuleResolutionKind.BaseUrl }; - } - + it ("load module as relative url", () => { function test(containingFileName: string, moduleFileName: string, moduleName: string): void { let containingFile = {name: containingFileName }; let moduleFile = { name: moduleFileName }; - let resolution = baseUrlModuleNameResolver(moduleName, containingFile.name, getCompilerOptions(""), createModuleResolutionHost(containingFile, moduleFile)); + let resolution = baseUrlModuleNameResolver(moduleName, containingFile.name, "", createModuleResolutionHost(containingFile, moduleFile)); assert.equal(resolution.resolvedFileName, moduleFile.name); + let expectedFailedLookupLocations: string[] = []; + + let moduleNameHasExt = forEach(supportedExtensions, e => fileExtensionIs(moduleName, e)); + if (!moduleNameHasExt) { + let dir = getDirectoryPath(containingFileName); + + // add candidates with extensions that precede extension of the actual module name file in the list of supportd extensions + for (let ext of supportedExtensions) { + + let hasExtension = ext !== ".ts" + ? fileExtensionIs(moduleFileName, ext) + : fileExtensionIs(moduleFileName, ".ts") && !fileExtensionIs(moduleFileName, ".d.ts"); + + if (hasExtension) { + break; + } + else { + expectedFailedLookupLocations.push(normalizePath(combinePaths(dir, moduleName + ext))); + } + } + } + + assert.deepEqual(resolution.failedLookupLocations, expectedFailedLookupLocations) } test("/a/b/c/d.ts", "/foo.ts", "/foo.ts"); @@ -250,5 +270,18 @@ module ts { test("/a/b/c/d.ts", "/a/b/c/foo.tsx", "foo.tsx"); test("/a/b/c/d.ts", "/a/b/c/foo.d.ts", "foo.d.ts"); }); + + it ("load module using base url", () => { + function test(containingFileName: string, moduleFileName: string, moduleName: string, baseUrl: string): void { + let containingFile = { name: containingFileName }; + let moduleFile = { name: moduleFileName }; + let resolution = baseUrlModuleNameResolver(moduleName, containingFileName, baseUrl, createModuleResolutionHost(containingFile, moduleFile)); + assert.equal(resolution.resolvedFileName, moduleFile.name); + } + + test("/a/base/c/d.ts", "/a/base/c/d/e.ts", "c/d/e", "/a/base"); + test("/a/base/c/d.ts", "/a/base/c/d/e.d.ts", "c/d/e", "/a/base"); + test("/a/base/c/d.ts", "/a/base/c/d/e.tsx", "c/d/e", "/a/base"); + }); }); } \ No newline at end of file