Merge pull request #16586 from Microsoft/projectRootAsCurrentDirectoryInEmit

Use the project root or the directory of current script info as the current directory to emit the file
This commit is contained in:
Sheetal Nandi
2017-10-10 10:28:14 -07:00
committed by GitHub
11 changed files with 109 additions and 66 deletions
+10 -3
View File
@@ -1092,11 +1092,18 @@ namespace ts {
return true;
}
if (defaultLibraryPath && defaultLibraryPath.length !== 0) {
return containsPath(defaultLibraryPath, file.path, currentDirectory, /*ignoreCase*/ !host.useCaseSensitiveFileNames());
if (!options.noLib) {
return false;
}
return compareStrings(file.fileName, getDefaultLibraryFileName(), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
// If '--lib' is not specified, include default library file according to '--target'
// otherwise, using options specified in '--lib' instead of '--target' default library file
if (!options.lib) {
return compareStrings(file.fileName, getDefaultLibraryFileName(), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
}
else {
return forEach(options.lib, libFileName => compareStrings(file.fileName, combinePaths(defaultLibraryPath, libFileName), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo);
}
}
function getDiagnosticsProducingTypeChecker() {
+44 -1
View File
@@ -563,7 +563,7 @@ namespace ts.projectSystem {
path: "/a/b/file3.js",
content: "console.log('file3');"
};
const externalProjectName = "externalproject";
const externalProjectName = "/a/b/externalproject";
const host = createServerHost([file1, file2, file3, libFile]);
const session = createSession(host);
const projectService = session.getProjectService();
@@ -588,5 +588,48 @@ namespace ts.projectSystem {
assert.isTrue(outFileContent.indexOf(file2.content) === -1);
assert.isTrue(outFileContent.indexOf(file3.content) === -1);
});
it("should use project root as current directory so that compile on save results in correct file mapping", () => {
const inputFileName = "Foo.ts";
const file1 = {
path: `/root/TypeScriptProject3/TypeScriptProject3/${inputFileName}`,
content: "consonle.log('file1');"
};
const externalProjectName = "/root/TypeScriptProject3/TypeScriptProject3/TypeScriptProject3.csproj";
const host = createServerHost([file1, libFile]);
const session = createSession(host);
const projectService = session.getProjectService();
const outFileName = "bar.js";
projectService.openExternalProject({
rootFiles: toExternalFiles([file1.path]),
options: {
outFile: outFileName,
sourceMap: true,
compileOnSave: true
},
projectFileName: externalProjectName
});
const emitRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(CommandNames.CompileOnSaveEmitFile, { file: file1.path });
session.executeCommand(emitRequest);
// Verify js file
const expectedOutFileName = "/root/TypeScriptProject3/TypeScriptProject3/" + outFileName;
assert.isTrue(host.fileExists(expectedOutFileName));
const outFileContent = host.readFile(expectedOutFileName);
verifyContentHasString(outFileContent, file1.content);
verifyContentHasString(outFileContent, `//# sourceMappingURL=${outFileName}.map`);
// Verify map file
const expectedMapFileName = expectedOutFileName + ".map";
assert.isTrue(host.fileExists(expectedMapFileName));
const mapFileContent = host.readFile(expectedMapFileName);
verifyContentHasString(mapFileContent, `"sources":["${inputFileName}"]`);
function verifyContentHasString(content: string, string: string) {
assert.isTrue(content.indexOf(string) !== -1, `Expected "${content}" to have "${string}"`);
}
});
});
}
+2 -2
View File
@@ -16,8 +16,8 @@ namespace ts.server {
directoryExists: () => false,
getDirectories: () => [],
createDirectory: noop,
getExecutingFilePath(): string { return void 0; },
getCurrentDirectory(): string { return void 0; },
getExecutingFilePath(): string { return ""; },
getCurrentDirectory(): string { return ""; },
getEnvironmentVariable(): string { return ""; },
readDirectory() { return []; },
exit: noop,
@@ -352,8 +352,6 @@ namespace ts.projectSystem {
verifyDiagnostics(actual, []);
}
const typeRootFromTsserverLocation = "/node_modules/@types";
export function getTypeRootsFromLocation(currentDirectory: string) {
currentDirectory = normalizePath(currentDirectory);
const result: string[] = [];
@@ -401,7 +399,7 @@ namespace ts.projectSystem {
const configFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]);
checkWatchedFiles(host, configFiles.concat(libFile.path, moduleFile.path));
checkWatchedDirectories(host, [], /*recursive*/ false);
checkWatchedDirectories(host, ["/a/b/c", typeRootFromTsserverLocation], /*recursive*/ true);
checkWatchedDirectories(host, ["/a/b/c", ...getTypeRootsFromLocation(getDirectoryPath(appFile.path))], /*recursive*/ true);
});
it("can handle tsconfig file name with difference casing", () => {
@@ -4331,7 +4329,7 @@ namespace ts.projectSystem {
function verifyCalledOnEachEntry(callback: CalledMaps, expectedKeys: Map<number>) {
const calledMap = calledMaps[callback];
assert.equal(calledMap.size, expectedKeys.size, `${callback}: incorrect size of map: Actual keys: ${arrayFrom(calledMap.keys())} Expected: ${arrayFrom(expectedKeys.keys())}`);
ts.TestFSWithWatch.verifyMapSize(callback, calledMap, arrayFrom(expectedKeys.keys()));
expectedKeys.forEach((called, name) => {
assert.isTrue(calledMap.has(name), `${callback} is expected to contain ${name}, actual keys: ${arrayFrom(calledMap.keys())}`);
assert.equal(calledMap.get(name).length, called, `${callback} is expected to be called ${called} times with ${name}. Actual entry: ${calledMap.get(name)}`);
@@ -4413,6 +4411,7 @@ namespace ts.projectSystem {
}
const f2Lookups = getLocationsForModuleLookup("f2");
callsTrackingHost.verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.fileExists, f2Lookups, 1);
const typeRootLocations = getTypeRootsFromLocation(getDirectoryPath(root.path));
const f2DirLookups = getLocationsForDirectoryLookup();
callsTrackingHost.verifyCalledOnEachEntry(CalledMapsWithSingleArg.directoryExists, f2DirLookups);
callsTrackingHost.verifyNoCall(CalledMapsWithSingleArg.getDirectories);
@@ -4423,7 +4422,7 @@ namespace ts.projectSystem {
verifyImportedDiagnostics();
const f1Lookups = f2Lookups.map(s => s.replace("f2", "f1"));
f1Lookups.length = f1Lookups.indexOf(imported.path) + 1;
const f1DirLookups = ["/c/d", "/c", typeRootFromTsserverLocation];
const f1DirLookups = ["/c/d", "/c", ...typeRootLocations];
vertifyF1Lookups();
// setting compiler options discards module resolution cache
@@ -4475,7 +4474,7 @@ namespace ts.projectSystem {
function getLocationsForDirectoryLookup() {
const result = createMap<number>();
// Type root
result.set(typeRootFromTsserverLocation, 1);
typeRootLocations.forEach(location => result.set(location, 1));
forEachAncestorDirectory(getDirectoryPath(root.path), ancestor => {
// To resolve modules
result.set(ancestor, 2);
+8 -4
View File
@@ -95,7 +95,7 @@ namespace ts.TestFSWithWatch {
}
}
function getDiffInKeys(map: Map<any>, expectedKeys: ReadonlyArray<string>) {
function getDiffInKeys<T>(map: Map<T>, expectedKeys: ReadonlyArray<string>) {
if (map.size === expectedKeys.length) {
return "";
}
@@ -122,8 +122,12 @@ namespace ts.TestFSWithWatch {
return `\n\nNotInActual: ${notInActual}\nDuplicates: ${duplicates}\nInActualButNotInExpected: ${inActualNotExpected}`;
}
function checkMapKeys(caption: string, map: Map<any>, expectedKeys: ReadonlyArray<string>) {
export function verifyMapSize(caption: string, map: Map<any>, expectedKeys: ReadonlyArray<string>) {
assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map: Actual keys: ${arrayFrom(map.keys())} Expected: ${expectedKeys}${getDiffInKeys(map, expectedKeys)}`);
}
function checkMapKeys(caption: string, map: Map<any>, expectedKeys: ReadonlyArray<string>) {
verifyMapSize(caption, map, expectedKeys);
for (const name of expectedKeys) {
assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`);
}
@@ -548,7 +552,7 @@ namespace ts.TestFSWithWatch {
const folder = this.toFolder(directoryName);
// base folder has to be present
const base = getDirectoryPath(folder.fullPath);
const base = getDirectoryPath(folder.path);
const baseFolder = this.fs.get(base) as Folder;
Debug.assert(isFolder(baseFolder));
@@ -560,7 +564,7 @@ namespace ts.TestFSWithWatch {
const file = this.toFile({ path, content });
// base folder has to be present
const base = getDirectoryPath(file.fullPath);
const base = getDirectoryPath(file.path);
const folder = this.fs.get(base) as Folder;
Debug.assert(isFolder(folder));
+15 -4
View File
@@ -403,7 +403,7 @@ namespace ts.server {
this.globalPlugins = opts.globalPlugins || emptyArray;
this.pluginProbeLocations = opts.pluginProbeLocations || emptyArray;
this.allowLocalPluginLoads = !!opts.allowLocalPluginLoads;
this.typesMapLocation = (opts.typesMapLocation === undefined) ? combinePaths(this.host.getExecutingFilePath(), "../typesMap.json") : opts.typesMapLocation;
this.typesMapLocation = (opts.typesMapLocation === undefined) ? combinePaths(this.getExecutingFilePath(), "../typesMap.json") : opts.typesMapLocation;
Debug.assert(!!this.host.createHash, "'ServerHost.createHash' is required for ProjectService");
@@ -452,6 +452,16 @@ namespace ts.server {
return toPath(fileName, this.currentDirectory, this.toCanonicalFileName);
}
/*@internal*/
getExecutingFilePath() {
return this.getNormalizedAbsolutePath(this.host.getExecutingFilePath());
}
/*@internal*/
getNormalizedAbsolutePath(fileName: string) {
return getNormalizedAbsolutePath(fileName, this.host.getCurrentDirectory());
}
/* @internal */
getChangedFiles_TestOnly() {
return this.changedFiles;
@@ -1626,12 +1636,13 @@ namespace ts.server {
return this.inferredProjects[0];
}
return this.createInferredProject(/*rootDirectoryForResolution*/ undefined, /*isSingleInferredProject*/ true);
// Single inferred project does not have a project root and hence no current directory
return this.createInferredProject(/*currentDirectory*/ undefined, /*isSingleInferredProject*/ true);
}
private createInferredProject(rootDirectoryForResolution: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: string): InferredProject {
private createInferredProject(currentDirectory: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: string): InferredProject {
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects;
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, rootDirectoryForResolution);
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, currentDirectory);
if (isSingleInferredProject) {
this.inferredProjects.unshift(project);
}
+18 -34
View File
@@ -195,6 +195,9 @@ namespace ts.server {
return result.module;
}
/*@internal*/
readonly currentDirectory: string;
/*@internal*/
constructor(
/*@internal*/readonly projectName: string,
@@ -206,7 +209,8 @@ namespace ts.server {
private compilerOptions: CompilerOptions,
public compileOnSaveEnabled: boolean,
/*@internal*/public directoryStructureHost: DirectoryStructureHost,
rootDirectoryForResolution: string | undefined) {
currentDirectory: string | undefined) {
this.currentDirectory = this.projectService.getNormalizedAbsolutePath(currentDirectory || "");
this.cancellationToken = new ThrottledCancellationToken(this.projectService.cancellationToken, this.projectService.throttleWaitMilliseconds);
if (!this.compilerOptions) {
@@ -230,7 +234,8 @@ namespace ts.server {
}
this.languageService = createLanguageService(this, this.documentRegistry);
this.resolutionCache = createResolutionCache(this, rootDirectoryForResolution);
// Use the current directory as resolution root only if the project created using current directory string
this.resolutionCache = createResolutionCache(this, currentDirectory && this.currentDirectory);
if (!languageServiceEnabled) {
this.disableLanguageService();
}
@@ -301,11 +306,11 @@ namespace ts.server {
}
getCurrentDirectory(): string {
return this.directoryStructureHost.getCurrentDirectory();
return this.currentDirectory;
}
getDefaultLibFileName() {
const nodeModuleBinDir = getDirectoryPath(normalizePath(this.projectService.host.getExecutingFilePath()));
const nodeModuleBinDir = getDirectoryPath(normalizePath(this.projectService.getExecutingFilePath()));
return combinePaths(nodeModuleBinDir, getDefaultLibFileName(this.compilerOptions));
}
@@ -448,9 +453,8 @@ namespace ts.server {
this.ensureBuilder();
const { emitSkipped, outputFiles } = this.builder.emitFile(this.program, scriptInfo.path);
if (!emitSkipped) {
const projectRootPath = this.getProjectRootPath();
for (const outputFile of outputFiles) {
const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, projectRootPath ? projectRootPath : getDirectoryPath(scriptInfo.fileName));
const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, this.currentDirectory);
writeFile(outputFileAbsoluteFileName, outputFile.text, outputFile.writeByteOrderMark);
}
}
@@ -479,7 +483,6 @@ namespace ts.server {
getProjectName() {
return this.projectName;
}
abstract getProjectRootPath(): string | undefined;
abstract getTypeAcquisition(): TypeAcquisition;
getExternalFiles(): SortedReadonlyArray<string> {
@@ -561,7 +564,7 @@ namespace ts.server {
return map(this.program.getSourceFiles(), sourceFile => {
const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.path);
if (!scriptInfo) {
Debug.fail(`scriptInfo for a file '${sourceFile.fileName}' is missing.`);
Debug.fail(`scriptInfo for a file '${sourceFile.fileName}' Path: '${sourceFile.path}' is missing.`);
}
return scriptInfo;
});
@@ -1042,8 +1045,8 @@ namespace ts.server {
projectService: ProjectService,
documentRegistry: DocumentRegistry,
compilerOptions: CompilerOptions,
public readonly projectRootPath: string | undefined,
rootDirectoryForResolution: string | undefined) {
readonly projectRootPath: string | undefined,
currentDirectory: string | undefined) {
super(InferredProject.newName(),
ProjectKind.Inferred,
projectService,
@@ -1053,7 +1056,7 @@ namespace ts.server {
compilerOptions,
/*compileOnSaveEnabled*/ false,
projectService.host,
rootDirectoryForResolution);
currentDirectory);
}
addRoot(info: ScriptInfo) {
@@ -1082,12 +1085,6 @@ namespace ts.server {
this.getRootScriptInfos().length === 1;
}
getProjectRootPath() {
return this.projectRootPath ||
// Single inferred project does not have a project root.
!this.projectService.useSingleInferredProject && getDirectoryPath(this.getRootFiles()[0]);
}
close() {
forEach(this.getRootScriptInfos(), info => this.projectService.stopWatchingConfigFilesForInferredProjectRoot(info));
super.close();
@@ -1183,7 +1180,7 @@ namespace ts.server {
// Search our peer node_modules, then any globally-specified probe paths
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
const searchPaths = [combinePaths(host.getExecutingFilePath(), "../../.."), ...this.projectService.pluginProbeLocations];
const searchPaths = [combinePaths(this.projectService.getExecutingFilePath(), "../../.."), ...this.projectService.pluginProbeLocations];
if (this.projectService.allowLocalPluginLoads) {
const local = getDirectoryPath(this.canonicalConfigFilePath);
@@ -1263,10 +1260,6 @@ namespace ts.server {
}
}
getProjectRootPath() {
return getDirectoryPath(this.getConfigFilePath());
}
/**
* Get the errors that dont have any file name associated
*/
@@ -1379,13 +1372,14 @@ namespace ts.server {
compilerOptions: CompilerOptions,
languageServiceEnabled: boolean,
public compileOnSaveEnabled: boolean,
private readonly projectFilePath?: string) {
projectFilePath?: string) {
super(externalProjectName,
ProjectKind.External,
projectService,
documentRegistry,
/*hasExplicitListOfFiles*/ true,
languageServiceEnabled, compilerOptions,
languageServiceEnabled,
compilerOptions,
compileOnSaveEnabled,
projectService.host,
getDirectoryPath(projectFilePath || normalizeSlashes(externalProjectName)));
@@ -1395,16 +1389,6 @@ namespace ts.server {
return this.excludedFiles;
}
getProjectRootPath() {
if (this.projectFilePath) {
return getDirectoryPath(this.projectFilePath);
}
// if the projectFilePath is not given, we make the assumption that the project name
// is the path of the project file. AS the project name is provided by VS, we need to
// normalize slashes before using it as a file name.
return getDirectoryPath(normalizeSlashes(this.getProjectName()));
}
getTypeAcquisition() {
return this.typeAcquisition;
}
+1 -6
View File
@@ -7141,7 +7141,6 @@ declare namespace ts.server {
enableLanguageService(): void;
disableLanguageService(): void;
getProjectName(): string;
abstract getProjectRootPath(): string | undefined;
abstract getTypeAcquisition(): TypeAcquisition;
getExternalFiles(): SortedReadonlyArray<string>;
getSourceFile(path: Path): SourceFile;
@@ -7194,7 +7193,6 @@ declare namespace ts.server {
addRoot(info: ScriptInfo): void;
removeRoot(info: ScriptInfo): void;
isProjectWithSingleRoot(): boolean;
getProjectRootPath(): string;
close(): void;
getTypeAcquisition(): TypeAcquisition;
}
@@ -7221,7 +7219,6 @@ declare namespace ts.server {
enablePlugins(): void;
private enablePlugin(pluginConfigEntry, searchPaths);
private enableProxy(pluginModuleFactory, configEntry);
getProjectRootPath(): string;
/**
* Get the errors that dont have any file name associated
*/
@@ -7247,11 +7244,9 @@ declare namespace ts.server {
class ExternalProject extends Project {
externalProjectName: string;
compileOnSaveEnabled: boolean;
private readonly projectFilePath;
excludedFiles: ReadonlyArray<NormalizedPath>;
private typeAcquisition;
getExcludedFiles(): ReadonlyArray<NormalizedPath>;
getProjectRootPath(): string;
getTypeAcquisition(): TypeAcquisition;
setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void;
}
@@ -7529,7 +7524,7 @@ declare namespace ts.server {
private updateNonInferredProject<T>(project, newUncheckedFiles, propertyReader, newOptions, newTypeAcquisition, compileOnSave);
private getOrCreateInferredProjectForProjectRootPathIfEnabled(info, projectRootPath);
private getOrCreateSingleInferredProjectIfEnabled();
private createInferredProject(rootDirectoryForResolution, isSingleInferredProject?, projectRootPath?);
private createInferredProject(currentDirectory, isSingleInferredProject?, projectRootPath?);
getScriptInfo(uncheckedFileName: string): ScriptInfo;
private watchClosedScriptInfo(info);
private stopWatchingScriptInfo(info);
@@ -14,11 +14,11 @@
////console.log("nothing");
goTo.file("a.ts")
verify.ProjectInfo(["lib.d.ts", "a.ts"])
verify.ProjectInfo(["/lib.d.ts", "a.ts"])
goTo.file("b.ts")
verify.ProjectInfo(["lib.d.ts", "a.ts", "b.ts"])
verify.ProjectInfo(["/lib.d.ts", "a.ts", "b.ts"])
goTo.file("c.ts")
verify.ProjectInfo(["lib.d.ts", "a.ts", "b.ts", "c.ts"])
verify.ProjectInfo(["/lib.d.ts", "a.ts", "b.ts", "c.ts"])
goTo.file("d.ts")
verify.ProjectInfo(["lib.d.ts", "d.ts"])
verify.ProjectInfo(["/lib.d.ts", "d.ts"])
@@ -10,4 +10,4 @@
////{ "files": ["a.ts", "b.ts"] }
goTo.file("a.ts")
verify.ProjectInfo(["lib.d.ts", "a.ts", "b.ts", "tsconfig.json"])
verify.ProjectInfo(["/lib.d.ts", "a.ts", "b.ts", "tsconfig.json"])
@@ -10,4 +10,4 @@
////{ "files": ["a.ts", "c.ts", "b.ts"] }
goTo.file("a.ts");
verify.ProjectInfo(["lib.d.ts", "a.ts", "b.ts", "tsconfig.json"])
verify.ProjectInfo(["/lib.d.ts", "a.ts", "b.ts", "tsconfig.json"])