diff --git a/Jakefile.js b/Jakefile.js index 4641df7f7b4a0..09958c48cde1c 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -700,7 +700,7 @@ task("generate-spec", [specMd]); // Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory desc("Makes a new LKG out of the built js files"); task("LKG", ["clean", "release", "local"].concat(libraryTargets), function () { - var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets); + var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, cancellationTokenFile, typingsInstallerFile].concat(libraryTargets); var missingFiles = expectedFiles.filter(function (f) { return !fs.existsSync(f); }); diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 9dd1247c8972e..117ca65cb64ca 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -627,5 +627,97 @@ namespace ts.projectSystem { checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path, commander.path, jquery.path, lodash.path, cordova.path]); checkProjectActualFiles(p2, [file3.path, grunt.path, gulp.path ]); }); + + it("configured projects discover from node_modules", () => { + const app = { + path: "/app.js", + content: "" + }; + const jsconfig = { + path: "/jsconfig.json", + content: JSON.stringify({}) + }; + const jquery = { + path: "/node_modules/jquery/index.js", + content: "" + }; + const jqueryPackage = { + path: "/node_modules/jquery/package.json", + content: JSON.stringify({ name: "jquery" }) + }; + const jqueryDTS = { + path: "/tmp/node_modules/@types/jquery/index.d.ts", + content: "" + }; + const host = createServerHost([app, jsconfig, jquery, jqueryPackage]); + const installer = new (class extends Installer { + constructor() { + super(host, { globalTypingsCacheLocation: "/tmp" }); + } + runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + const installedTypings = ["@types/jquery"]; + const typingFiles = [jqueryDTS]; + executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + } + })(); + + const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer }); + projectService.openClientFile(app.path); + + checkNumberOfProjects(projectService, { configuredProjects: 1 }); + const p = projectService.configuredProjects[0]; + checkProjectActualFiles(p, [app.path]); + + installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + + checkNumberOfProjects(projectService, { configuredProjects: 1 }); + checkProjectActualFiles(p, [app.path, jqueryDTS.path]); + }); + + it("configured projects discover from bower.josn", () => { + const app = { + path: "/app.js", + content: "" + }; + const jsconfig = { + path: "/jsconfig.json", + content: JSON.stringify({}) + }; + const bowerJson = { + path: "/bower.json", + content: JSON.stringify({ + "dependencies": { + "jquery": "^3.1.0" + } + }) + }; + const jqueryDTS = { + path: "/tmp/node_modules/@types/jquery/index.d.ts", + content: "" + }; + const host = createServerHost([app, jsconfig, bowerJson]); + const installer = new (class extends Installer { + constructor() { + super(host, { globalTypingsCacheLocation: "/tmp" }); + } + runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + const installedTypings = ["@types/jquery"]; + const typingFiles = [jqueryDTS]; + executeCommand(this, host, installedTypings, typingFiles, requestKind, cb); + } + })(); + + const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer }); + projectService.openClientFile(app.path); + + checkNumberOfProjects(projectService, { configuredProjects: 1 }); + const p = projectService.configuredProjects[0]; + checkProjectActualFiles(p, [app.path]); + + installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]); + + checkNumberOfProjects(projectService, { configuredProjects: 1 }); + checkProjectActualFiles(p, [app.path, jqueryDTS.path]); + }); }); } \ No newline at end of file diff --git a/src/server/typingsInstaller/nodeTypingsInstaller.ts b/src/server/typingsInstaller/nodeTypingsInstaller.ts index 7df1e9a78776f..b73a8e4e79db3 100644 --- a/src/server/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/server/typingsInstaller/nodeTypingsInstaller.ts @@ -9,6 +9,7 @@ namespace ts.server.typingsInstaller { const path: { join(...parts: string[]): string; dirname(path: string): string; + basename(path: string, extension?: string): string; } = require("path"); class FileLog implements Log { @@ -23,6 +24,15 @@ namespace ts.server.typingsInstaller { } } + function getNPMLocation(processName: string) { + if (path.basename(processName).indexOf("node") == 0) { + return `"${path.join(path.dirname(process.argv[0]), "npm")}"`; + } + else { + return "npm"; + } + } + export class NodeTypingsInstaller extends TypingsInstaller { private readonly exec: { (command: string, options: { cwd: string }, callback?: (error: Error, stdout: string, stderr: string) => void): any }; @@ -31,7 +41,7 @@ namespace ts.server.typingsInstaller { constructor(globalTypingsCacheLocation: string, throttleLimit: number, log: Log) { super( globalTypingsCacheLocation, - /*npmPath*/ `"${path.join(path.dirname(process.argv[0]), "npm")}"`, + /*npmPath*/ getNPMLocation(process.argv[0]), toPath("typingSafeList.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)), throttleLimit, log); diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 1113e6e2701dd..5437328b581bf 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -150,7 +150,7 @@ namespace ts.server.typingsInstaller { if (this.installTypingHost.fileExists(packageJson)) { const npmConfig = JSON.parse(this.installTypingHost.readFile(packageJson)); if (this.log.isEnabled()) { - this.log.writeLine(`Loaded content of '${npmConfig}': ${JSON.stringify(npmConfig)}`); + this.log.writeLine(`Loaded content of '${packageJson}': ${JSON.stringify(npmConfig)}`); } if (npmConfig.devDependencies) { for (const key in npmConfig.devDependencies) { diff --git a/src/server/utilities.ts b/src/server/utilities.ts index a7a0160ea62d1..69962fa186ea8 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -35,7 +35,7 @@ namespace ts.server { function getProjectRootPath(project: Project): Path { switch (project.projectKind) { case ProjectKind.Configured: - return project.getProjectName(); + return getDirectoryPath(project.getProjectName()); case ProjectKind.Inferred: // TODO: fixme return ""; diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 32670c738fa04..d50e26c61941b 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -187,7 +187,7 @@ namespace ts.JsTyping { } const typingNames: string[] = []; - const fileNames = host.readDirectory(nodeModulesPath, ["*.json"], /*excludes*/ undefined, /*includes*/ undefined, /*depth*/ 2); + const fileNames = host.readDirectory(nodeModulesPath, [".json"], /*excludes*/ undefined, /*includes*/ undefined, /*depth*/ 2); for (const fileName of fileNames) { const normalizedFileName = normalizePath(fileName); if (getBaseFileName(normalizedFileName) !== "package.json") {