Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use CompilerHost.realpath to resolve actual location for symlinks #8486

Merged
merged 2 commits into from
May 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2752,6 +2752,10 @@
"category": "Error",
"code": 6129
},
"Resolving real path for '{0}', result '{1}'": {
"category": "Message",
"code": 6130
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
Expand Down
33 changes: 21 additions & 12 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,22 +570,29 @@ namespace ts {
let resolvedFileName = tryLoadModuleUsingOptionalResolutionSettings(moduleName, containingDirectory, nodeLoadModuleByRelativeName,
failedLookupLocations, supportedExtensions, state);

if (resolvedFileName) {
return createResolvedModule(resolvedFileName, /*isExternalLibraryImport*/false, failedLookupLocations);
let isExternalLibraryImport = false;
if (!resolvedFileName) {
if (moduleHasNonRelativeName(moduleName)) {
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
}
resolvedFileName = loadModuleFromNodeModules(moduleName, containingDirectory, failedLookupLocations, state);
isExternalLibraryImport = resolvedFileName !== undefined;
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
resolvedFileName = nodeLoadModuleByRelativeName(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}
}

let isExternalLibraryImport = false;
if (moduleHasNonRelativeName(moduleName)) {
if (resolvedFileName && host.realpath) {
const originalFileName = resolvedFileName;
resolvedFileName = normalizePath(host.realpath(resolvedFileName));
if (traceEnabled) {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder, moduleName);
trace(host, Diagnostics.Resolving_real_path_for_0_result_1, originalFileName, resolvedFileName);
}
resolvedFileName = loadModuleFromNodeModules(moduleName, containingDirectory, failedLookupLocations, state);
isExternalLibraryImport = resolvedFileName !== undefined;
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
resolvedFileName = nodeLoadModuleByRelativeName(candidate, supportedExtensions, failedLookupLocations, /*onlyRecordFailures*/ false, state);
}

return createResolvedModule(resolvedFileName, isExternalLibraryImport, failedLookupLocations);
}

Expand Down Expand Up @@ -873,6 +880,7 @@ namespace ts {
}

const newLine = getNewLineCharacter(options);
const realpath = sys.realpath && ((path: string) => sys.realpath(path));

return {
getSourceFile,
Expand All @@ -886,7 +894,8 @@ namespace ts {
fileExists: fileName => sys.fileExists(fileName),
readFile: fileName => sys.readFile(fileName),
trace: (s: string) => sys.write(s + newLine),
directoryExists: directoryName => sys.directoryExists(directoryName)
directoryExists: directoryName => sys.directoryExists(directoryName),
realpath
};
}

Expand Down
8 changes: 7 additions & 1 deletion src/compiler/sys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace ts {
createHash?(data: string): string;
getMemoryUsage?(): number;
exit(exitCode?: number): void;
realpath?(path: string): string;
}

export interface FileWatcher {
Expand Down Expand Up @@ -73,6 +74,7 @@ namespace ts {
readDirectory(path: string, extension?: string, exclude?: string[]): string[];
watchFile?(path: string, callback: FileWatcherCallback): FileWatcher;
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
realpath(path: string): string;
};

export var sys: System = (function () {
Expand Down Expand Up @@ -527,12 +529,15 @@ namespace ts {
},
exit(exitCode?: number): void {
process.exit(exitCode);
},
realpath(path: string): string {
return _fs.realpathSync(path);
}
};
}

function getChakraSystem(): System {

const realpath = ChakraHost.realpath && ((path: string) => ChakraHost.realpath(path));
return {
newLine: ChakraHost.newLine || "\r\n",
args: ChakraHost.args,
Expand All @@ -558,6 +563,7 @@ namespace ts {
getCurrentDirectory: () => ChakraHost.currentDirectory,
readDirectory: ChakraHost.readDirectory,
exit: ChakraHost.quit,
realpath
};
}

Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2779,6 +2779,7 @@ namespace ts {
readFile(fileName: string): string;
trace?(s: string): void;
directoryExists?(directoryName: string): boolean;
realpath?(path: string): string;
}

export interface ResolvedModule {
Expand Down
6 changes: 3 additions & 3 deletions src/harness/compilerRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,16 @@ class CompilerBaselineRunner extends RunnerBase {
otherFiles = [];

if (testCaseContent.settings["noImplicitReferences"] || /require\(/.test(lastUnit.content) || /reference\spath/.test(lastUnit.content)) {
toBeCompiled.push({ unitName: this.makeUnitName(lastUnit.name, rootDir), content: lastUnit.content });
toBeCompiled.push({ unitName: this.makeUnitName(lastUnit.name, rootDir), content: lastUnit.content, fileOptions: lastUnit.fileOptions });
units.forEach(unit => {
if (unit.name !== lastUnit.name) {
otherFiles.push({ unitName: this.makeUnitName(unit.name, rootDir), content: unit.content });
otherFiles.push({ unitName: this.makeUnitName(unit.name, rootDir), content: unit.content, fileOptions: unit.fileOptions });
}
});
}
else {
toBeCompiled = units.map(unit => {
return { unitName: this.makeUnitName(unit.name, rootDir), content: unit.content };
return { unitName: this.makeUnitName(unit.name, rootDir), content: unit.content, fileOptions: unit.fileOptions };
});
}

Expand Down
39 changes: 27 additions & 12 deletions src/harness/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -855,21 +855,31 @@ namespace Harness {
// Local get canonical file name function, that depends on passed in parameter for useCaseSensitiveFileNames
const getCanonicalFileName = ts.createGetCanonicalFileName(useCaseSensitiveFileNames);

const fileMap: ts.FileMap<ts.SourceFile> = ts.createFileMap<ts.SourceFile>();
let realPathMap: ts.FileMap<string>;
const fileMap: ts.FileMap<() => ts.SourceFile> = ts.createFileMap<() => ts.SourceFile>();
for (const file of inputFiles) {
if (file.content !== undefined) {
const fileName = ts.normalizePath(file.unitName);
const sourceFile = createSourceFileAndAssertInvariants(fileName, file.content, scriptTarget);
const path = ts.toPath(file.unitName, currentDirectory, getCanonicalFileName);
fileMap.set(path, sourceFile);
if (file.fileOptions && file.fileOptions["symlink"]) {
const link = file.fileOptions["symlink"];
const linkPath = ts.toPath(link, currentDirectory, getCanonicalFileName);
if (!realPathMap) {
realPathMap = ts.createFileMap<string>();
}
realPathMap.set(linkPath, fileName);
fileMap.set(path, (): ts.SourceFile => { throw new Error("Symlinks should always be resolved to a realpath first"); });
}
const sourceFile = createSourceFileAndAssertInvariants(fileName, file.content, scriptTarget);
fileMap.set(path, () => sourceFile);
}
}

function getSourceFile(fileName: string, languageVersion: ts.ScriptTarget) {
fileName = ts.normalizePath(fileName);
const path = ts.toPath(fileName, currentDirectory, getCanonicalFileName);
if (fileMap.contains(path)) {
return fileMap.get(path);
return fileMap.get(path)();
}
else if (fileName === fourslashFileName) {
const tsFn = "tests/cases/fourslash/" + fourslashFileName;
Expand Down Expand Up @@ -898,11 +908,16 @@ namespace Harness {
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
getNewLine: () => newLine,
fileExists: fileName => {
return fileMap.contains(ts.toPath(fileName, currentDirectory, getCanonicalFileName));
const path = ts.toPath(fileName, currentDirectory, getCanonicalFileName);
return fileMap.contains(path) || (realPathMap && realPathMap.contains(path));
},
readFile: (fileName: string): string => {
return fileMap.get(ts.toPath(fileName, currentDirectory, getCanonicalFileName)).getText();
}
return fileMap.get(ts.toPath(fileName, currentDirectory, getCanonicalFileName))().getText();
},
realpath: realPathMap && ((f: string) => {
const path = ts.toPath(f, currentDirectory, getCanonicalFileName);
return realPathMap.contains(path) ? realPathMap.get(path) : path;
})
};
}

Expand All @@ -923,7 +938,8 @@ namespace Harness {
{ name: "libFiles", type: "string" },
{ name: "noErrorTruncation", type: "boolean" },
{ name: "suppressOutputPathCheck", type: "boolean" },
{ name: "noImplicitReferences", type: "boolean" }
{ name: "noImplicitReferences", type: "boolean" },
{ name: "symlink", type: "string" }
];

let optionsIndex: ts.Map<ts.CommandLineOption>;
Expand Down Expand Up @@ -978,6 +994,7 @@ namespace Harness {
export interface TestFile {
unitName: string;
content: string;
fileOptions?: any;
}

export interface CompilationOutput {
Expand Down Expand Up @@ -1415,10 +1432,8 @@ namespace Harness {
// Comment line, check for global/file @options and record them
optionRegex.lastIndex = 0;
const metaDataName = testMetaData[1].toLowerCase();
if (metaDataName === "filename") {
currentFileOptions[testMetaData[1]] = testMetaData[2];
}
else {
currentFileOptions[testMetaData[1]] = testMetaData[2];
if (metaDataName !== "filename") {
continue;
}

Expand Down
4 changes: 4 additions & 0 deletions src/services/shims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,11 +431,15 @@ namespace ts {
export class CoreServicesShimHostAdapter implements ParseConfigHost, ModuleResolutionHost {

public directoryExists: (directoryName: string) => boolean;
public realpath: (path: string) => string;

constructor(private shimHost: CoreServicesShimHost) {
if ("directoryExists" in this.shimHost) {
this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
}
if ("realpath" in this.shimHost) {
this.realpath = path => this.shimHost.realpath(path);
}
}

public readDirectory(rootDir: string, extension: string, exclude: string[], depth?: number): string[] {
Expand Down
37 changes: 37 additions & 0 deletions tests/baselines/reference/moduleResolutionWithSymlinks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//// [tests/cases/compiler/moduleResolutionWithSymlinks.ts] ////

//// [index.ts]

export class MyClass{}

//// [index.ts]
import {MyClass} from "library-a";
export { MyClass as MyClass2 }

//// [app.ts]
import { MyClass } from "./library-a";
import { MyClass2 } from "./library-b";

let x: MyClass;
let y: MyClass2;
x = y;
y = x;

//// [index.js]
"use strict";
var MyClass = (function () {
function MyClass() {
}
return MyClass;
}());
exports.MyClass = MyClass;
//// [index.js]
"use strict";
var library_a_1 = require("library-a");
exports.MyClass2 = library_a_1.MyClass;
//// [app.js]
"use strict";
var x;
var y;
x = y;
y = x;
36 changes: 36 additions & 0 deletions tests/baselines/reference/moduleResolutionWithSymlinks.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
=== /src/app.ts ===
import { MyClass } from "./library-a";
>MyClass : Symbol(MyClass, Decl(app.ts, 0, 8))

import { MyClass2 } from "./library-b";
>MyClass2 : Symbol(MyClass2, Decl(app.ts, 1, 8))

let x: MyClass;
>x : Symbol(x, Decl(app.ts, 3, 3))
>MyClass : Symbol(MyClass, Decl(app.ts, 0, 8))

let y: MyClass2;
>y : Symbol(y, Decl(app.ts, 4, 3))
>MyClass2 : Symbol(MyClass2, Decl(app.ts, 1, 8))

x = y;
>x : Symbol(x, Decl(app.ts, 3, 3))
>y : Symbol(y, Decl(app.ts, 4, 3))

y = x;
>y : Symbol(y, Decl(app.ts, 4, 3))
>x : Symbol(x, Decl(app.ts, 3, 3))

=== /src/library-a/index.ts ===

export class MyClass{}
>MyClass : Symbol(MyClass, Decl(index.ts, 0, 0))

=== /src/library-b/index.ts ===
import {MyClass} from "library-a";
>MyClass : Symbol(MyClass, Decl(index.ts, 0, 8))

export { MyClass as MyClass2 }
>MyClass : Symbol(MyClass2, Decl(index.ts, 1, 8))
>MyClass2 : Symbol(MyClass2, Decl(index.ts, 1, 8))

32 changes: 32 additions & 0 deletions tests/baselines/reference/moduleResolutionWithSymlinks.trace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[
"======== Resolving module './library-a' from '/src/app.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module as file / folder, candidate module location '/src/library-a'.",
"File '/src/library-a.ts' does not exist.",
"File '/src/library-a.tsx' does not exist.",
"File '/src/library-a.d.ts' does not exist.",
"File '/src/library-a/package.json' does not exist.",
"File '/src/library-a/index.ts' exist - use it as a name resolution result.",
"Resolving real path for '/src/library-a/index.ts', result '/src/library-a/index.ts'",
"======== Module name './library-a' was successfully resolved to '/src/library-a/index.ts'. ========",
"======== Resolving module './library-b' from '/src/app.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module as file / folder, candidate module location '/src/library-b'.",
"File '/src/library-b.ts' does not exist.",
"File '/src/library-b.tsx' does not exist.",
"File '/src/library-b.d.ts' does not exist.",
"File '/src/library-b/package.json' does not exist.",
"File '/src/library-b/index.ts' exist - use it as a name resolution result.",
"Resolving real path for '/src/library-b/index.ts', result '/src/library-b/index.ts'",
"======== Module name './library-b' was successfully resolved to '/src/library-b/index.ts'. ========",
"======== Resolving module 'library-a' from '/src/library-b/index.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module 'library-a' from 'node_modules' folder.",
"File '/src/library-b/node_modules/library-a.ts' does not exist.",
"File '/src/library-b/node_modules/library-a.tsx' does not exist.",
"File '/src/library-b/node_modules/library-a.d.ts' does not exist.",
"File '/src/library-b/node_modules/library-a/package.json' does not exist.",
"File '/src/library-b/node_modules/library-a/index.ts' exist - use it as a name resolution result.",
"Resolving real path for '/src/library-b/node_modules/library-a/index.ts', result '/src/library-a/index.ts'",
"======== Module name 'library-a' was successfully resolved to '/src/library-a/index.ts'. ========"
]
38 changes: 38 additions & 0 deletions tests/baselines/reference/moduleResolutionWithSymlinks.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
=== /src/app.ts ===
import { MyClass } from "./library-a";
>MyClass : typeof MyClass

import { MyClass2 } from "./library-b";
>MyClass2 : typeof MyClass

let x: MyClass;
>x : MyClass
>MyClass : MyClass

let y: MyClass2;
>y : MyClass
>MyClass2 : MyClass

x = y;
>x = y : MyClass
>x : MyClass
>y : MyClass

y = x;
>y = x : MyClass
>y : MyClass
>x : MyClass

=== /src/library-a/index.ts ===

export class MyClass{}
>MyClass : MyClass

=== /src/library-b/index.ts ===
import {MyClass} from "library-a";
>MyClass : typeof MyClass

export { MyClass as MyClass2 }
>MyClass : typeof MyClass
>MyClass2 : typeof MyClass

Loading