diff --git a/.gitmodules b/.gitmodules
index f7632c4abbd..fdf474a693d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -18,7 +18,19 @@
path = tests/cases/user/TypeScript-WeChat-Starter/TypeScript-WeChat-Starter
url = https://github.com/Microsoft/TypeScript-WeChat-Starter.git
ignore = all
+[submodule "tests/cases/user/create-react-app/create-react-app"]
+ path = tests/cases/user/create-react-app/create-react-app
+ url = https://github.com/facebook/create-react-app.git
+ ignore = all
[submodule "tests/cases/user/webpack/webpack"]
path = tests/cases/user/webpack/webpack
url = https://github.com/webpack/webpack.git
ignore = all
+[submodule "tests/cases/user/puppeteer/puppeteer"]
+ path = tests/cases/user/puppeteer/puppeteer
+ url = https://github.com/GoogleChrome/puppeteer.git
+ ignore = all
+[submodule "tests/cases/user/axios-src/axios-src"]
+ path = tests/cases/user/axios-src/axios-src
+ url = https://github.com/axios/axios.git
+ ignore = all
diff --git a/Gulpfile.ts b/Gulpfile.js
similarity index 85%
rename from Gulpfile.ts
rename to Gulpfile.js
index 1c6cfdd7ed1..afa7e775dcd 100644
--- a/Gulpfile.ts
+++ b/Gulpfile.js
@@ -1,35 +1,27 @@
///
-import * as cp from "child_process";
-import * as path from "path";
-import * as fs from "fs";
-import child_process = require("child_process");
-import originalGulp = require("gulp");
-import helpMaker = require("gulp-help");
-import runSequence = require("run-sequence");
-import concat = require("gulp-concat");
-import clone = require("gulp-clone");
-import newer = require("gulp-newer");
-import tsc = require("gulp-typescript");
-declare module "gulp-typescript" {
- interface Settings {
- pretty?: boolean;
- newLine?: string;
- noImplicitThis?: boolean;
- stripInternal?: boolean;
- types?: string[];
- }
-}
-import * as insert from "gulp-insert";
-import * as sourcemaps from "gulp-sourcemaps";
-import Q = require("q");
-import del = require("del");
-import mkdirP = require("mkdirp");
-import minimist = require("minimist");
-import browserify = require("browserify");
-import through2 = require("through2");
-import merge2 = require("merge2");
-import * as os from "os";
-import fold = require("travis-fold");
+// @ts-check
+const cp = require("child_process");
+const path = require("path");
+const fs = require("fs");
+const child_process = require("child_process");
+const originalGulp = require("gulp");
+const helpMaker = require("gulp-help");
+const runSequence = require("run-sequence");
+const concat = require("gulp-concat");
+const clone = require("gulp-clone");
+const newer = require("gulp-newer");
+const tsc = require("gulp-typescript");
+const insert = require("gulp-insert");
+const sourcemaps = require("gulp-sourcemaps");
+const Q = require("q");
+const del = require("del");
+const mkdirP = require("mkdirp");
+const minimist = require("minimist");
+const browserify = require("browserify");
+const through2 = require("through2");
+const merge2 = require("merge2");
+const os = require("os");
+const fold = require("travis-fold");
const gulp = helpMaker(originalGulp);
Error.stackTraceLimit = 1000;
@@ -73,17 +65,26 @@ const cmdLineOptions = minimist(process.argv.slice(2), {
});
const noop = () => {}; // tslint:disable-line no-empty
-function exec(cmd: string, args: string[], complete: () => void = noop, error: (e: any, status: number) => void = noop) {
+/**
+ * @param {string} cmd
+ * @param {string[]} args
+ * @param {() => void} complete
+ * @param {(e: *, status: number) => void} error
+ */
+function exec(cmd, args, complete = noop, error = noop) {
console.log(`${cmd} ${args.join(" ")}`);
// TODO (weswig): Update child_process types to add windowsVerbatimArguments to the type definition
const subshellFlag = isWin ? "/c" : "-c";
const command = isWin ? [possiblyQuote(cmd), ...args] : [`${cmd} ${args.join(" ")}`];
- const ex = cp.spawn(isWin ? "cmd" : "/bin/sh", [subshellFlag, ...command], { stdio: "inherit", windowsVerbatimArguments: true } as any);
+ const ex = cp.spawn(isWin ? "cmd" : "/bin/sh", [subshellFlag, ...command], { stdio: "inherit", windowsVerbatimArguments: true });
ex.on("exit", (code) => code === 0 ? complete() : error(/*e*/ undefined, code));
ex.on("error", error);
}
-function possiblyQuote(cmd: string) {
+/**
+ * @param {string} cmd
+ */
+function possiblyQuote(cmd) {
return cmd.indexOf(" ") >= 0 ? `"${cmd}"` : cmd;
}
@@ -215,12 +216,17 @@ for (const i in libraryTargets) {
.pipe(gulp.dest(".")));
}
-const configureNightlyJs = path.join(scriptsDirectory, "configureNightly.js");
-const configureNightlyTs = path.join(scriptsDirectory, "configureNightly.ts");
+const configurePreleleaseJs = path.join(scriptsDirectory, "configurePrerelease.js");
+const configurePreleleaseTs = path.join(scriptsDirectory, "configurePrerelease.ts");
const packageJson = "package.json";
const versionFile = path.join(compilerDirectory, "core.ts");
-function needsUpdate(source: string | string[], dest: string | string[]): boolean {
+/**
+ * @param {string | string[]} source
+ * @param {string | string[]} dest
+ * @returns {boolean}
+ */
+function needsUpdate(source, dest) {
if (typeof source === "string" && typeof dest === "string") {
if (fs.existsSync(dest)) {
const {mtime: outTime} = fs.statSync(dest);
@@ -283,8 +289,13 @@ function needsUpdate(source: string | string[], dest: string | string[]): boolea
return true;
}
-function getCompilerSettings(base: tsc.Settings, useBuiltCompiler?: boolean): tsc.Settings {
- const copy: tsc.Settings = {};
+/**
+ * @param {tsc.Settings} base
+ * @param {boolean=} useBuiltCompiler
+ * @returns {tsc.Settings}
+ */
+function getCompilerSettings(base, useBuiltCompiler) {
+ const copy = /** @type {tsc.Settings} */ ({});
for (const key in base) {
copy[key] = base[key];
}
@@ -293,32 +304,34 @@ function getCompilerSettings(base: tsc.Settings, useBuiltCompiler?: boolean): ts
}
copy.newLine = "lf";
if (useBuiltCompiler === true) {
- copy.typescript = require("./built/local/typescript.js");
+ copy.typescript = /** @type {*} */ (require("./built/local/typescript.js"));
}
else if (useBuiltCompiler === false) {
- copy.typescript = require("./lib/typescript.js");
+ copy.typescript = /** @type {*} */ (require("./lib/typescript.js"));
}
return copy;
}
-gulp.task(configureNightlyJs, /*help*/ false, [], () => {
- const settings: tsc.Settings = {
+gulp.task(configurePreleleaseJs, /*help*/ false, [], () => {
+ /** @type {tsc.Settings} */
+ const settings = {
declaration: false,
removeComments: true,
noResolve: false,
stripInternal: false,
+ module: "commonjs"
};
- return gulp.src(configureNightlyTs)
+ return gulp.src(configurePreleleaseTs)
.pipe(sourcemaps.init())
.pipe(tsc(settings))
- .pipe(sourcemaps.write(path.dirname(configureNightlyJs)))
- .pipe(gulp.dest(path.dirname(configureNightlyJs)));
+ .pipe(sourcemaps.write("."))
+ .pipe(gulp.dest("./scripts"));
});
// Nightly management tasks
-gulp.task("configure-nightly", "Runs scripts/configureNightly.ts to prepare a build for nightly publishing", [configureNightlyJs], (done) => {
- exec(host, [configureNightlyJs, packageJson, versionFile], done, done);
+gulp.task("configure-nightly", "Runs scripts/configurePrerelease.ts to prepare a build for nightly publishing", [configurePreleleaseJs], (done) => {
+ exec(host, [configurePreleleaseJs, "dev", packageJson, versionFile], done, done);
});
gulp.task("publish-nightly", "Runs `npm publish --tag next` to create a new nightly build on npm", ["LKG"], () => {
return runSequence("clean", "useDebugMode", "runtests-parallel", (done) => {
@@ -331,7 +344,8 @@ const importDefinitelyTypedTestsJs = path.join(importDefinitelyTypedTestsDirecto
const importDefinitelyTypedTestsTs = path.join(importDefinitelyTypedTestsDirectory, "importDefinitelyTypedTests.ts");
gulp.task(importDefinitelyTypedTestsJs, /*help*/ false, [], () => {
- const settings: tsc.Settings = getCompilerSettings({
+ /** @type {tsc.Settings} */
+ const settings = getCompilerSettings({
declaration: false,
removeComments: true,
noResolve: false,
@@ -362,20 +376,11 @@ const builtGeneratedDiagnosticMessagesJSON = path.join(builtLocalDirectory, "dia
// processDiagnosticMessages script
gulp.task(processDiagnosticMessagesJs, /*help*/ false, [], () => {
- const settings: tsc.Settings = getCompilerSettings({
- target: "es5",
- declaration: false,
- removeComments: true,
- noResolve: false,
- stripInternal: false,
- outFile: processDiagnosticMessagesJs
- }, /*useBuiltCompiler*/ false);
- return gulp.src(processDiagnosticMessagesTs)
+ const diagsProject = tsc.createProject('./scripts/processDiagnosticMessages.tsconfig.json');
+ return diagsProject.src()
.pipe(newer(processDiagnosticMessagesJs))
- .pipe(sourcemaps.init())
- .pipe(tsc(settings))
- .pipe(sourcemaps.write("."))
- .pipe(gulp.dest("."));
+ .pipe(diagsProject())
+ .pipe(gulp.dest(scriptsDirectory));
});
// The generated diagnostics map; built for the compiler and for the "generate-diagnostics" task
@@ -402,7 +407,8 @@ const generateLocalizedDiagnosticMessagesJs = path.join(scriptsDirectory, "gener
const generateLocalizedDiagnosticMessagesTs = path.join(scriptsDirectory, "generateLocalizedDiagnosticMessages.ts");
gulp.task(generateLocalizedDiagnosticMessagesJs, /*help*/ false, [], () => {
- const settings: tsc.Settings = getCompilerSettings({
+ /** @type {tsc.Settings} */
+ const settings = getCompilerSettings({
target: "es5",
declaration: false,
removeComments: true,
@@ -433,8 +439,12 @@ const nodePackageFile = path.join(builtLocalDirectory, "typescript.js");
const nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts");
const nodeStandaloneDefinitionsFile = path.join(builtLocalDirectory, "typescript_standalone.d.ts");
-let copyrightContent: string;
-function prependCopyright(outputCopyright: boolean = !useDebugMode) {
+/** @type {string} */
+let copyrightContent;
+/**
+ * @param {boolean} outputCopyright
+ */
+function prependCopyright(outputCopyright = !useDebugMode) {
return insert.prepend(outputCopyright ? (copyrightContent || (copyrightContent = fs.readFileSync(copyright).toString())) : "");
}
@@ -526,9 +536,10 @@ const tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverli
gulp.task(tsserverLibraryFile, /*help*/ false, [servicesFile, typesMapJson], (done) => {
const serverLibraryProject = tsc.createProject("src/server/tsconfig.library.json", getCompilerSettings({ removeComments: false }, /*useBuiltCompiler*/ true));
- const {js, dts}: { js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream } = serverLibraryProject.src()
+ /** @type {{ js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream }} */
+ const {js, dts} = serverLibraryProject.src()
.pipe(sourcemaps.init())
- .pipe(newer({ dest: tsserverLibraryFile, extra: ["src/compiler/**/*.ts", "src/services/**/*.ts"] }))
+ .pipe(newer(/** @type {*} */({ dest: tsserverLibraryFile, extra: ["src/compiler/**/*.ts", "src/services/**/*.ts"] })))
.pipe(serverLibraryProject());
return merge2([
@@ -563,7 +574,8 @@ const specWord = path.join(docDirectory, "TypeScript Language Specification.docx
const specMd = path.join(docDirectory, "spec.md");
gulp.task(word2mdJs, /*help*/ false, [], () => {
- const settings: tsc.Settings = getCompilerSettings({
+ /** @type {tsc.Settings} */
+ const settings = getCompilerSettings({
outFile: word2mdJs
}, /*useBuiltCompiler*/ false);
return gulp.src(word2mdTs)
@@ -642,7 +654,8 @@ function deleteTemporaryProjectOutput() {
return del(path.join(localBaseline, "projectOutput/"));
}
-let savedNodeEnv: string;
+/** @type {string} */
+let savedNodeEnv;
function setNodeEnvToDevelopment() {
savedNodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = "development";
@@ -652,7 +665,12 @@ function restoreSavedNodeEnv() {
process.env.NODE_ENV = savedNodeEnv;
}
-function runConsoleTests(defaultReporter: string, runInParallel: boolean, done: (e?: any) => void) {
+/**
+ * @param {string} defaultReporter
+ * @param {boolean} runInParallel
+ * @param {(e?: any) => void} done
+ */
+function runConsoleTests(defaultReporter, runInParallel, done) {
const lintFlag = cmdLineOptions.lint;
cleanTestDirs((err) => {
if (err) { console.error(err); failWithStatus(err, 1); }
@@ -727,7 +745,11 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
}
});
- function failWithStatus(err?: any, status?: number) {
+ /**
+ * @param {any=} err
+ * @param {number=} status
+ */
+ function failWithStatus(err, status) {
if (err || status) {
process.exit(typeof status === "number" ? status : 2);
}
@@ -743,7 +765,11 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
}
}
- function finish(error?: any, errorStatus?: number) {
+ /**
+ * @param {any=} error
+ * @param {number=} errorStatus
+ */
+ function finish(error, errorStatus) {
restoreSavedNodeEnv();
deleteTestConfig().then(deleteTemporaryProjectOutput).then(() => {
if (error !== undefined || errorStatus !== undefined) {
@@ -773,7 +799,8 @@ gulp.task("runtests",
const nodeServerOutFile = "tests/webTestServer.js";
const nodeServerInFile = "tests/webTestServer.ts";
gulp.task(nodeServerOutFile, /*help*/ false, [servicesFile], () => {
- const settings: tsc.Settings = getCompilerSettings({ module: "commonjs" }, /*useBuiltCompiler*/ true);
+ /** @type {tsc.Settings} */
+ const settings = getCompilerSettings({ module: "commonjs" }, /*useBuiltCompiler*/ true);
return gulp.src(nodeServerInFile)
.pipe(newer(nodeServerOutFile))
.pipe(sourcemaps.init())
@@ -782,16 +809,18 @@ gulp.task(nodeServerOutFile, /*help*/ false, [servicesFile], () => {
.pipe(gulp.dest(path.dirname(nodeServerOutFile)));
});
-import convertMap = require("convert-source-map");
-import sorcery = require("sorcery");
-import Vinyl = require("vinyl");
+const convertMap = require("convert-source-map");
+const sorcery = require("sorcery");
+const Vinyl = require("vinyl");
const bundlePath = path.resolve("built/local/bundle.js");
gulp.task("browserify", "Runs browserify on run.js to produce a file suitable for running tests in the browser", [servicesFile], (done) => {
const testProject = tsc.createProject("src/harness/tsconfig.json", getCompilerSettings({ outFile: bundlePath, inlineSourceMap: true }, /*useBuiltCompiler*/ true));
- let originalMap: any;
- let prebundledContent: string;
+ /** @type {*} */
+ let originalMap;
+ /** @type {string} */
+ let prebundledContent;
browserify(testProject.src()
.pipe(newer(bundlePath))
.pipe(sourcemaps.init())
@@ -855,8 +884,10 @@ gulp.task("browserify", "Runs browserify on run.js to produce a file suitable fo
});
});
-
-function cleanTestDirs(done: (e?: any) => void) {
+/**
+ * @param {(e?: any) => void} done
+ */
+function cleanTestDirs(done) {
// Clean the local baselines & Rwc baselines directories
del([
localBaseline,
@@ -872,8 +903,17 @@ function cleanTestDirs(done: (e?: any) => void) {
});
}
-// used to pass data from jake command line directly to run.js
-function writeTestConfigFile(tests: string, runners: string, light: boolean, taskConfigsFolder?: string, workerCount?: number, stackTraceLimit?: string, timeout?: number) {
+/**
+ * used to pass data from jake command line directly to run.js
+ * @param {string} tests
+ * @param {string} runners
+ * @param {boolean} light
+ * @param {string=} taskConfigsFolder
+ * @param {number=} workerCount
+ * @param {string=} stackTraceLimit
+ * @param {number=} timeout
+ */
+function writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, timeout) {
const testConfigContents = JSON.stringify({
test: tests ? [tests] : undefined,
runner: runners ? runners.split(",") : undefined,
@@ -974,7 +1014,7 @@ gulp.task("baseline-accept-test262", "Makes the most recent test262 test results
const webhostPath = "tests/webhost/webtsc.ts";
const webhostJsPath = "tests/webhost/webtsc.js";
gulp.task(webhostJsPath, /*help*/ false, [servicesFile], () => {
- const settings: tsc.Settings = getCompilerSettings({
+ const settings = getCompilerSettings({
outFile: webhostJsPath
}, /*useBuiltCompiler*/ true);
return gulp.src(webhostPath)
@@ -994,7 +1034,7 @@ gulp.task("webhost", "Builds the tsc web host", [webhostJsPath], () => {
const perftscPath = "tests/perftsc.ts";
const perftscJsPath = "built/local/perftsc.js";
gulp.task(perftscJsPath, /*help*/ false, [servicesFile], () => {
- const settings: tsc.Settings = getCompilerSettings({
+ const settings = getCompilerSettings({
outFile: perftscJsPath
}, /*useBuiltCompiler*/ true);
return gulp.src(perftscPath)
@@ -1025,7 +1065,7 @@ gulp.task(loggedIOJsPath, /*help*/ false, [], (done) => {
const instrumenterPath = path.join(harnessDirectory, "instrumenter.ts");
const instrumenterJsPath = path.join(builtLocalDirectory, "instrumenter.js");
gulp.task(instrumenterJsPath, /*help*/ false, [servicesFile], () => {
- const settings: tsc.Settings = getCompilerSettings({
+ const settings = getCompilerSettings({
module: "commonjs",
target: "es5",
lib: [
@@ -1052,7 +1092,7 @@ gulp.task("update-sublime", "Updates the sublime plugin's tsserver", ["local", s
});
gulp.task("build-rules", "Compiles tslint rules to js", () => {
- const settings: tsc.Settings = getCompilerSettings({ module: "commonjs", lib: ["es6"] }, /*useBuiltCompiler*/ false);
+ const settings = getCompilerSettings({ module: "commonjs", lib: ["es6"] }, /*useBuiltCompiler*/ false);
const dest = path.join(builtLocalDirectory, "tslint");
return gulp.src("scripts/tslint/**/*.ts")
.pipe(newer({
@@ -1065,51 +1105,6 @@ gulp.task("build-rules", "Compiles tslint rules to js", () => {
.pipe(gulp.dest(dest));
});
-const lintTargets = [
- "Gulpfile.ts",
- "src/compiler/**/*.ts",
- "src/harness/**/*.ts",
- "!src/harness/unittests/services/formatting/**/*.ts",
- "src/server/**/*.ts",
- "scripts/tslint/**/*.ts",
- "src/services/**/*.ts",
- "tests/*.ts", "tests/webhost/*.ts" // Note: does *not* descend recursively
-];
-
-function sendNextFile(files: {path: string}[], child: cp.ChildProcess, callback: (failures: number) => void, failures: number) {
- const file = files.pop();
- if (file) {
- console.log(`Linting '${file.path}'.`);
- child.send({ kind: "file", name: file.path });
- }
- else {
- child.send({ kind: "close" });
- callback(failures);
- }
-}
-
-function spawnLintWorker(files: {path: string}[], callback: (failures: number) => void) {
- const child = cp.fork("./scripts/parallel-lint");
- let failures = 0;
- child.on("message", data => {
- switch (data.kind) {
- case "result":
- if (data.failures > 0) {
- failures += data.failures;
- console.log(data.output);
- }
- sendNextFile(files, child, callback, failures);
- break;
- case "error":
- console.error(data.error);
- failures++;
- sendNextFile(files, child, callback, failures);
- break;
- }
- });
- sendNextFile(files, child, callback, failures);
-}
-
gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: --f[iles]=regex", ["build-rules"], () => {
if (fold.isTravis()) console.log(fold.start("lint"));
for (const project of ["scripts/tslint/tsconfig.json", "src/tsconfig-base.json"]) {
diff --git a/Jakefile.js b/Jakefile.js
index bda270b0b4c..3eb8736445b 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -491,7 +491,7 @@ compileFile(/*outfile*/configurePrereleaseJs,
/*prereqs*/[configurePrereleaseTs],
/*prefixes*/[],
/*useBuiltCompiler*/ false,
- { noOutFile: false, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false });
+ { noOutFile: true, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false });
task("setDebugMode", function () {
useDebugMode = true;
diff --git a/package.json b/package.json
index 5837f72317f..71b5ed71029 100644
--- a/package.json
+++ b/package.json
@@ -76,7 +76,6 @@
"source-map-support": "latest",
"through2": "latest",
"travis-fold": "latest",
- "ts-node": "latest",
"tslint": "latest",
"vinyl": "latest",
"chalk": "latest",
diff --git a/scripts/configurePrerelease.ts b/scripts/configurePrerelease.ts
index d17ddb963b1..da1984c13e0 100644
--- a/scripts/configurePrerelease.ts
+++ b/scripts/configurePrerelease.ts
@@ -1,4 +1,9 @@
-///
+///
+import { normalize } from "path";
+import assert = require("assert");
+import { readFileSync, writeFileSync } from "fs";
+const args = process.argv.slice(2);
+
/**
* A minimal description for a parsed package.json object.
@@ -10,28 +15,27 @@ interface PackageJson {
}
function main(): void {
- const sys = ts.sys;
- if (sys.args.length < 3) {
- sys.write("Usage:" + sys.newLine)
- sys.write("\tnode configureNightly.js " + sys.newLine);
+ if (args.length < 3) {
+ console.log("Usage:");
+ console.log("\tnode configureNightly.js ");
return;
}
- const tag = sys.args[0];
+ const tag = args[0];
if (tag !== "dev" && tag !== "insiders") {
throw new Error(`Unexpected tag name '${tag}'.`);
}
// Acquire the version from the package.json file and modify it appropriately.
- const packageJsonFilePath = ts.normalizePath(sys.args[1]);
- const packageJsonValue: PackageJson = JSON.parse(sys.readFile(packageJsonFilePath));
+ const packageJsonFilePath = normalize(args[1]);
+ const packageJsonValue: PackageJson = JSON.parse(readFileSync(packageJsonFilePath).toString());
const { majorMinor, patch } = parsePackageJsonVersion(packageJsonValue.version);
const prereleasePatch = getPrereleasePatch(tag, patch);
// Acquire and modify the source file that exposes the version string.
- const tsFilePath = ts.normalizePath(sys.args[2]);
- const tsFileContents = ts.sys.readFile(tsFilePath);
+ const tsFilePath = normalize(args[2]);
+ const tsFileContents = readFileSync(tsFilePath).toString();
const modifiedTsFileContents = updateTsFile(tsFilePath, tsFileContents, majorMinor, patch, prereleasePatch);
// Ensure we are actually changing something - the user probably wants to know that the update failed.
@@ -44,20 +48,20 @@ function main(): void {
// Finally write the changes to disk.
// Modify the package.json structure
packageJsonValue.version = `${majorMinor}.${prereleasePatch}`;
- sys.writeFile(packageJsonFilePath, JSON.stringify(packageJsonValue, /*replacer:*/ undefined, /*space:*/ 4))
- sys.writeFile(tsFilePath, modifiedTsFileContents);
+ writeFileSync(packageJsonFilePath, JSON.stringify(packageJsonValue, /*replacer:*/ undefined, /*space:*/ 4))
+ writeFileSync(tsFilePath, modifiedTsFileContents);
}
function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: string, patch: string, nightlyPatch: string): string {
const majorMinorRgx = /export const versionMajorMinor = "(\d+\.\d+)"/;
const majorMinorMatch = majorMinorRgx.exec(tsFileContents);
- ts.Debug.assert(majorMinorMatch !== null, "", () => `The file seems to no longer have a string matching '${majorMinorRgx}'.`);
+ assert(majorMinorMatch !== null, `The file seems to no longer have a string matching '${majorMinorRgx}'.`);
const parsedMajorMinor = majorMinorMatch[1];
- ts.Debug.assert(parsedMajorMinor === majorMinor, "versionMajorMinor does not match.", () => `${tsFilePath}: '${parsedMajorMinor}'; package.json: '${majorMinor}'`);
+ assert(parsedMajorMinor === majorMinor, `versionMajorMinor does not match. ${tsFilePath}: '${parsedMajorMinor}'; package.json: '${majorMinor}'`);
const versionRgx = /export const version = `\$\{versionMajorMinor\}\.(\d)(-dev)?`;/;
const patchMatch = versionRgx.exec(tsFileContents);
- ts.Debug.assert(patchMatch !== null, "The file seems to no longer have a string matching", () => versionRgx.toString());
+ assert(patchMatch !== null, "The file seems to no longer have a string matching " + versionRgx.toString());
const parsedPatch = patchMatch[1];
if (parsedPatch !== patch) {
throw new Error(`patch does not match. ${tsFilePath}: '${parsedPatch}; package.json: '${patch}'`);
@@ -69,7 +73,7 @@ function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: st
function parsePackageJsonVersion(versionString: string): { majorMinor: string, patch: string } {
const versionRgx = /(\d+\.\d+)\.(\d+)($|\-)/;
const match = versionString.match(versionRgx);
- ts.Debug.assert(match !== null, "package.json 'version' should match", () => versionRgx.toString());
+ assert(match !== null, "package.json 'version' should match " + versionRgx.toString());
return { majorMinor: match[1], patch: match[2] };
}
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index d009a956c43..f497ba82e47 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -24465,7 +24465,10 @@ namespace ts {
checkImportBinding(importClause.namedBindings);
}
else {
- forEach(importClause.namedBindings.elements, checkImportBinding);
+ const moduleExisted = resolveExternalModuleName(node, node.moduleSpecifier);
+ if (moduleExisted) {
+ forEach(importClause.namedBindings.elements, checkImportBinding);
+ }
}
}
}
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index 0bef14e9dda..27e906b8a5a 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -2994,18 +2994,19 @@ namespace ts {
}
/** Remove the *first* occurrence of `item` from the array. */
- export function unorderedRemoveItem(array: T[], item: T): void {
- unorderedRemoveFirstItemWhere(array, element => element === item);
+ export function unorderedRemoveItem(array: T[], item: T) {
+ return unorderedRemoveFirstItemWhere(array, element => element === item);
}
/** Remove the *first* element satisfying `predicate`. */
- function unorderedRemoveFirstItemWhere(array: T[], predicate: (element: T) => boolean): void {
+ function unorderedRemoveFirstItemWhere(array: T[], predicate: (element: T) => boolean) {
for (let i = 0; i < array.length; i++) {
if (predicate(array[i])) {
unorderedRemoveItemAt(array, i);
- break;
+ return true;
}
}
+ return false;
}
export type GetCanonicalFileName = (fileName: string) => string;
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index c665744b1ea..235788258af 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -2943,10 +2943,6 @@
"category": "Message",
"code": 6040
},
- "Compilation complete. Watching for file changes.": {
- "category": "Message",
- "code": 6042
- },
"Generates corresponding '.map' file.": {
"category": "Message",
"code": 6043
@@ -3522,11 +3518,11 @@
"code": 6192,
"reportsUnnecessary": true
},
- "Found 1 error.": {
+ "Found 1 error. Watching for file changes.": {
"category": "Message",
"code": 6193
},
- "Found {0} errors.": {
+ "Found {0} errors. Watching for file changes.": {
"category": "Message",
"code": 6194
},
diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts
index c2b96649cf2..35e056546c0 100644
--- a/src/compiler/resolutionCache.ts
+++ b/src/compiler/resolutionCache.ts
@@ -10,6 +10,7 @@ namespace ts {
invalidateResolutionOfFile(filePath: Path): void;
removeResolutionsOfFile(filePath: Path): void;
+ setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: Map>): void;
createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution;
startCachingPerDirectoryResolution(): void;
@@ -74,6 +75,7 @@ namespace ts {
export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string, logChangesWhenResolvingModule: boolean): ResolutionCache {
let filesWithChangedSetOfUnresolvedImports: Path[] | undefined;
let filesWithInvalidatedResolutions: Map | undefined;
+ let filesWithInvalidatedNonRelativeUnresolvedImports: Map> | undefined;
let allFilesHaveInvalidatedResolution = false;
const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory());
@@ -122,6 +124,7 @@ namespace ts {
resolveTypeReferenceDirectives,
removeResolutionsOfFile,
invalidateResolutionOfFile,
+ setFilesWithInvalidatedNonRelativeUnresolvedImports,
createHasInvalidatedResolution,
updateTypeRootsWatch,
closeTypeRootsWatch,
@@ -165,6 +168,16 @@ namespace ts {
return collected;
}
+ function isFileWithInvalidatedNonRelativeUnresolvedImports(path: Path) {
+ if (!filesWithInvalidatedNonRelativeUnresolvedImports) {
+ return false;
+ }
+
+ // Invalidated if file has unresolved imports
+ const value = filesWithInvalidatedNonRelativeUnresolvedImports.get(path);
+ return value && !!value.length;
+ }
+
function createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution {
if (allFilesHaveInvalidatedResolution || forceAllFilesAsInvalidated) {
// Any file asked would have invalidated resolution
@@ -173,7 +186,8 @@ namespace ts {
}
const collected = filesWithInvalidatedResolutions;
filesWithInvalidatedResolutions = undefined;
- return path => collected && collected.has(path);
+ return path => (collected && collected.has(path)) ||
+ isFileWithInvalidatedNonRelativeUnresolvedImports(path);
}
function clearPerDirectoryResolutions() {
@@ -184,6 +198,7 @@ namespace ts {
function finishCachingPerDirectoryResolution() {
allFilesHaveInvalidatedResolution = false;
+ filesWithInvalidatedNonRelativeUnresolvedImports = undefined;
directoryWatchesOfFailedLookups.forEach((watcher, path) => {
if (watcher.refCount === 0) {
directoryWatchesOfFailedLookups.delete(path);
@@ -237,13 +252,15 @@ namespace ts {
const resolvedModules: R[] = [];
const compilerOptions = resolutionHost.getCompilationSettings();
-
+ const hasInvalidatedNonRelativeUnresolvedImport = logChanges && isFileWithInvalidatedNonRelativeUnresolvedImports(path);
const seenNamesInFile = createMap();
for (const name of names) {
let resolution = resolutionsInFile.get(name);
// Resolution is valid if it is present and not invalidated
if (!seenNamesInFile.has(name) &&
- allFilesHaveInvalidatedResolution || !resolution || resolution.isInvalidated) {
+ allFilesHaveInvalidatedResolution || !resolution || resolution.isInvalidated ||
+ // If the name is unresolved import that was invalidated, recalculate
+ (hasInvalidatedNonRelativeUnresolvedImport && !isExternalModuleNameRelative(name) && !getResolutionWithResolvedFileName(resolution))) {
const existingResolution = resolution;
const resolutionInDirectory = perDirectoryResolution.get(name);
if (resolutionInDirectory) {
@@ -284,7 +301,7 @@ namespace ts {
if (oldResolution === newResolution) {
return true;
}
- if (!oldResolution || !newResolution || oldResolution.isInvalidated) {
+ if (!oldResolution || !newResolution) {
return false;
}
const oldResult = getResolutionWithResolvedFileName(oldResolution);
@@ -577,6 +594,11 @@ namespace ts {
);
}
+ function setFilesWithInvalidatedNonRelativeUnresolvedImports(filesMap: Map>) {
+ Debug.assert(filesWithInvalidatedNonRelativeUnresolvedImports === filesMap || filesWithInvalidatedNonRelativeUnresolvedImports === undefined);
+ filesWithInvalidatedNonRelativeUnresolvedImports = filesMap;
+ }
+
function invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath: Path, isCreatingWatchedDirectory: boolean) {
let isChangedFailedLookupLocation: (location: string) => boolean;
if (isCreatingWatchedDirectory) {
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 5eea26f1c69..7fabd5d0265 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -1892,7 +1892,7 @@ namespace ts {
kind: SyntaxKind.DebuggerStatement;
}
- export interface MissingDeclaration extends DeclarationStatement, ClassElement, ObjectLiteralElement, TypeElement {
+ export interface MissingDeclaration extends DeclarationStatement {
kind: SyntaxKind.MissingDeclaration;
name?: Identifier;
}
@@ -3193,7 +3193,8 @@ namespace ts {
export type AnyValidImportOrReExport =
| (ImportDeclaration | ExportDeclaration) & { moduleSpecifier: StringLiteral }
| ImportEqualsDeclaration & { moduleReference: ExternalModuleReference & { expression: StringLiteral } }
- | RequireOrImportCall;
+ | RequireOrImportCall
+ | ImportTypeNode & { argument: LiteralType };
/* @internal */
export type RequireOrImportCall = CallExpression & { arguments: [StringLiteralLike] };
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index f975e27dca3..3fe255fb612 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -1710,8 +1710,10 @@ namespace ts {
return (node.parent as ExternalModuleReference).parent as AnyValidImportOrReExport;
case SyntaxKind.CallExpression:
return node.parent as AnyValidImportOrReExport;
+ case SyntaxKind.LiteralType:
+ return cast(node.parent.parent, isImportTypeNode) as ImportTypeNode & { argument: LiteralType };
default:
- return Debug.fail(Debug.showSyntaxKind(node));
+ return Debug.fail(Debug.showSyntaxKind(node.parent));
}
}
@@ -4926,6 +4928,10 @@ namespace ts {
return node.kind === SyntaxKind.LiteralType;
}
+ export function isImportTypeNode(node: Node): node is ImportTypeNode {
+ return node.kind === SyntaxKind.ImportType;
+ }
+
// Binding patterns
export function isObjectBindingPattern(node: Node): node is ObjectBindingPattern {
@@ -5606,8 +5612,7 @@ namespace ts {
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor
|| kind === SyntaxKind.IndexSignature
- || kind === SyntaxKind.SemicolonClassElement
- || kind === SyntaxKind.MissingDeclaration;
+ || kind === SyntaxKind.SemicolonClassElement;
}
export function isClassLike(node: Node): node is ClassLikeDeclaration {
@@ -5638,8 +5643,7 @@ namespace ts {
|| kind === SyntaxKind.CallSignature
|| kind === SyntaxKind.PropertySignature
|| kind === SyntaxKind.MethodSignature
- || kind === SyntaxKind.IndexSignature
- || kind === SyntaxKind.MissingDeclaration;
+ || kind === SyntaxKind.IndexSignature;
}
export function isClassOrTypeElement(node: Node): node is ClassElement | TypeElement {
@@ -5653,8 +5657,7 @@ namespace ts {
|| kind === SyntaxKind.SpreadAssignment
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
- || kind === SyntaxKind.SetAccessor
- || kind === SyntaxKind.MissingDeclaration;
+ || kind === SyntaxKind.SetAccessor;
}
// Type
diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts
index 7f3d078b790..37aa0118801 100644
--- a/src/compiler/watch.ts
+++ b/src/compiler/watch.ts
@@ -29,9 +29,8 @@ namespace ts {
/** @internal */
export const nonClearingMessageCodes: number[] = [
- Diagnostics.Compilation_complete_Watching_for_file_changes.code,
- Diagnostics.Found_1_error.code,
- Diagnostics.Found_0_errors.code
+ Diagnostics.Found_1_error_Watching_for_file_changes.code,
+ Diagnostics.Found_0_errors_Watching_for_file_changes.code
];
function clearScreenIfNotWatchingForFileChanges(system: System, diagnostic: Diagnostic, options: CompilerOptions) {
@@ -231,10 +230,10 @@ namespace ts {
const reportSummary = (errorCount: number) => {
if (errorCount === 1) {
- onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_1_error, errorCount), newLine, compilerOptions);
+ onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes, errorCount), newLine, compilerOptions);
}
else {
- onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_0_errors, errorCount, errorCount), newLine, compilerOptions);
+ onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount, errorCount), newLine, compilerOptions);
}
};
@@ -644,7 +643,7 @@ namespace ts {
if (host.afterProgramCreate) {
host.afterProgramCreate(builderProgram);
}
- reportWatchDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes);
+
return builderProgram;
}
diff --git a/src/harness/externalCompileRunner.ts b/src/harness/externalCompileRunner.ts
index 1f945cfcc9c..3d07c69af85 100644
--- a/src/harness/externalCompileRunner.ts
+++ b/src/harness/externalCompileRunner.ts
@@ -66,7 +66,7 @@ abstract class ExternalCompileRunnerBase extends RunnerBase {
if (fs.existsSync(path.join(cwd, "node_modules"))) {
require("del").sync(path.join(cwd, "node_modules"), { force: true });
}
- const install = cp.spawnSync(`npm`, ["i"], { cwd, timeout: timeout / 2, shell: true, stdio }); // NPM shouldn't take the entire timeout - if it takes a long time, it should be terminated and we should log the failure
+ const install = cp.spawnSync(`npm`, ["i", "--ignore-scripts"], { cwd, timeout: timeout / 2, shell: true, stdio }); // NPM shouldn't take the entire timeout - if it takes a long time, it should be terminated and we should log the failure
if (install.status !== 0) throw new Error(`NPM Install for ${directoryName} failed: ${install.stderr.toString()}`);
}
const args = [path.join(__dirname, "tsc.js")];
diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index b479b5596a8..75915c915ed 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -2103,14 +2103,11 @@ Actual: ${stringify(fullActual)}`);
this.raiseError("verifyRangesInImplementationList failed - expected to find at least one implementation location but got 0");
}
- for (let i = 0; i < implementations.length; i++) {
- for (let j = 0; j < implementations.length; j++) {
- if (i !== j && implementationsAreEqual(implementations[i], implementations[j])) {
- const { textSpan, fileName } = implementations[i];
- const end = textSpan.start + textSpan.length;
- this.raiseError(`Duplicate implementations returned for range (${textSpan.start}, ${end}) in ${fileName}`);
- }
- }
+ const duplicate = findDuplicatedElement(implementations, implementationsAreEqual);
+ if (duplicate) {
+ const { textSpan, fileName } = duplicate;
+ const end = textSpan.start + textSpan.length;
+ this.raiseError(`Duplicate implementations returned for range (${textSpan.start}, ${end}) in ${fileName}`);
}
const ranges = this.getRanges();
@@ -2910,6 +2907,7 @@ Actual: ${stringify(fullActual)}`);
}
private verifyDocumentHighlights(expectedRanges: Range[], fileNames: ReadonlyArray = [this.activeFile.fileName]) {
+ fileNames = ts.map(fileNames, ts.normalizePath);
const documentHighlights = this.getDocumentHighlightsAtCurrentPosition(fileNames) || [];
for (const dh of documentHighlights) {
@@ -2919,7 +2917,7 @@ Actual: ${stringify(fullActual)}`);
}
for (const fileName of fileNames) {
- const expectedRangesInFile = expectedRanges.filter(r => r.fileName === fileName);
+ const expectedRangesInFile = expectedRanges.filter(r => ts.normalizePath(r.fileName) === fileName);
const highlights = ts.find(documentHighlights, dh => dh.fileName === fileName);
const spansInFile = highlights ? highlights.highlightSpans.sort((s1, s2) => s1.textSpan.start - s2.textSpan.start) : [];
@@ -3219,14 +3217,14 @@ Actual: ${stringify(fullActual)}`);
}
}
else if (ts.isString(indexOrName)) {
- let name = indexOrName;
+ let name = ts.normalizePath(indexOrName);
// names are stored in the compiler with this relative path, this allows people to use goTo.file on just the fileName
name = name.indexOf("/") === -1 ? (this.basePath + "/" + name) : name;
const availableNames: string[] = [];
const result = ts.forEach(this.testData.files, file => {
- const fn = file.fileName;
+ const fn = ts.normalizePath(file.fileName);
if (fn) {
if (fn === name) {
return file;
@@ -3755,6 +3753,16 @@ ${code}
function stripWhitespace(s: string): string {
return s.replace(/\s/g, "");
}
+
+ function findDuplicatedElement(a: ReadonlyArray, equal: (a: T, b: T) => boolean): T {
+ for (let i = 0; i < a.length; i++) {
+ for (let j = i + 1; j < a.length; j++) {
+ if (equal(a[i], a[j])) {
+ return a[i];
+ }
+ }
+ }
+ }
}
namespace FourSlashInterface {
diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json
index 0d56447b28a..3ad653403cf 100644
--- a/src/harness/tsconfig.json
+++ b/src/harness/tsconfig.json
@@ -112,10 +112,8 @@
"../services/codefixes/fixInvalidImportSyntax.ts",
"../services/codefixes/fixStrictClassInitialization.ts",
"../services/codefixes/useDefaultImport.ts",
- "../services/codefixes/fixes.ts",
"../services/refactors/extractSymbol.ts",
"../services/refactors/generateGetAccessorAndSetAccessor.ts",
- "../services/refactors/refactors.ts",
"../services/sourcemaps.ts",
"../services/services.ts",
"../services/breakpoints.ts",
diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts
index 99bad3e3091..c8faba855dc 100644
--- a/src/harness/unittests/tscWatchMode.ts
+++ b/src/harness/unittests/tscWatchMode.ts
@@ -130,8 +130,8 @@ namespace ts.tscWatch {
function createErrorsFoundCompilerDiagnostic(errors: ReadonlyArray) {
return errors.length === 1
- ? createCompilerDiagnostic(Diagnostics.Found_1_error)
- : createCompilerDiagnostic(Diagnostics.Found_0_errors, errors.length);
+ ? createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes)
+ : createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errors.length);
}
function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) {
@@ -142,8 +142,7 @@ namespace ts.tscWatch {
logsBeforeErrors,
errors,
disableConsoleClears,
- createErrorsFoundCompilerDiagnostic(errors),
- createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes));
+ createErrorsFoundCompilerDiagnostic(errors));
}
function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
@@ -154,8 +153,7 @@ namespace ts.tscWatch {
logsBeforeErrors,
errors,
disableConsoleClears,
- createErrorsFoundCompilerDiagnostic(errors),
- createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes));
+ createErrorsFoundCompilerDiagnostic(errors));
}
function checkOutputErrorsIncrementalWithExit(host: WatchedSystem, errors: ReadonlyArray, expectedExitCode: ExitStatus, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts
index b167ed21b94..10912b4fb87 100644
--- a/src/harness/unittests/tsserverProjectSystem.ts
+++ b/src/harness/unittests/tsserverProjectSystem.ts
@@ -7294,7 +7294,6 @@ namespace ts.projectSystem {
const host = createServerHost(files);
const session = createSession(host);
const projectService = session.getProjectService();
- debugger;
session.executeCommandSeq({
command: protocol.CommandTypes.Open,
arguments: {
diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts
index 1a19c98f477..b6d5a20ef9a 100644
--- a/src/harness/unittests/typingsInstaller.ts
+++ b/src/harness/unittests/typingsInstaller.ts
@@ -999,14 +999,14 @@ namespace ts.projectSystem {
proj.updateGraph();
assert.deepEqual(
- proj.getCachedUnresolvedImportsPerFile_TestOnly().get(f1.path),
+ proj.cachedUnresolvedImportsPerFile.get(f1.path),
["foo", "foo", "foo", "@bar/router", "@bar/common", "@bar/common"]
);
installer.installAll(/*expectedCount*/ 1);
});
- it("should recompute resolutions after typings are installed", () => {
+ it("cached unresolved typings are not recomputed if program structure did not change", () => {
const host = createServerHost([]);
const session = createSession(host);
const f = {
@@ -1029,7 +1029,7 @@ namespace ts.projectSystem {
const projectService = session.getProjectService();
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const proj = projectService.inferredProjects[0];
- const version1 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion();
+ const version1 = proj.lastCachedUnresolvedImportsList;
// make a change that should not affect the structure of the program
const changeRequest: server.protocol.ChangeRequest = {
@@ -1047,8 +1047,8 @@ namespace ts.projectSystem {
};
session.executeCommand(changeRequest);
host.checkTimeoutQueueLengthAndRun(2); // This enqueues the updategraph and refresh inferred projects
- const version2 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion();
- assert.notEqual(version1, version2, "set of unresolved imports should change");
+ const version2 = proj.lastCachedUnresolvedImportsList;
+ assert.strictEqual(version1, version2, "set of unresolved imports should change");
});
it("expired cache entry (inferred project, should install typings)", () => {
@@ -1621,4 +1621,75 @@ namespace ts.projectSystem {
assert.deepEqual(commands, expectedCommands, "commands");
});
});
+
+ describe("recomputing resolutions of unresolved imports", () => {
+ const globalTypingsCacheLocation = "/tmp";
+ const appPath = "/a/b/app.js" as Path;
+ const foooPath = "/a/b/node_modules/fooo/index.d.ts";
+ function verifyResolvedModuleOfFooo(project: server.Project) {
+ const foooResolution = project.getLanguageService().getProgram().getSourceFileByPath(appPath).resolvedModules.get("fooo");
+ assert.equal(foooResolution.resolvedFileName, foooPath);
+ return foooResolution;
+ }
+
+ function verifyUnresolvedImportResolutions(appContents: string, typingNames: string[], typingFiles: FileOrFolder[]) {
+ const app: FileOrFolder = {
+ path: appPath,
+ content: `${appContents}import * as x from "fooo";`
+ };
+ const fooo: FileOrFolder = {
+ path: foooPath,
+ content: `export var x: string;`
+ };
+ const host = createServerHost([app, fooo]);
+ const installer = new (class extends Installer {
+ constructor() {
+ super(host, { globalTypingsCacheLocation, typesRegistry: createTypesRegistry("foo") });
+ }
+ installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
+ executeCommand(this, host, typingNames, typingFiles, cb);
+ }
+ })();
+ const projectService = createProjectService(host, { typingsInstaller: installer });
+ projectService.openClientFile(app.path);
+ projectService.checkNumberOfProjects({ inferredProjects: 1 });
+
+ const proj = projectService.inferredProjects[0];
+ checkProjectActualFiles(proj, [app.path, fooo.path]);
+ const foooResolution1 = verifyResolvedModuleOfFooo(proj);
+
+ installer.installAll(/*expectedCount*/ 1);
+ host.checkTimeoutQueueLengthAndRun(2);
+ checkProjectActualFiles(proj, typingFiles.map(f => f.path).concat(app.path, fooo.path));
+ const foooResolution2 = verifyResolvedModuleOfFooo(proj);
+ assert.strictEqual(foooResolution1, foooResolution2);
+ }
+
+ it("correctly invalidate the resolutions with typing names", () => {
+ verifyUnresolvedImportResolutions('import * as a from "foo";', ["foo"], [{
+ path: `${globalTypingsCacheLocation}/node_modules/foo/index.d.ts`,
+ content: "export function a(): void;"
+ }]);
+ });
+
+ it("correctly invalidate the resolutions with typing names that are trimmed", () => {
+ const fooAA: FileOrFolder = {
+ path: `${globalTypingsCacheLocation}/node_modules/foo/a/a.d.ts`,
+ content: "export function a (): void;"
+ };
+ const fooAB: FileOrFolder = {
+ path: `${globalTypingsCacheLocation}/node_modules/foo/a/b.d.ts`,
+ content: "export function b (): void;"
+ };
+ const fooAC: FileOrFolder = {
+ path: `${globalTypingsCacheLocation}/node_modules/foo/a/c.d.ts`,
+ content: "export function c (): void;"
+ };
+ verifyUnresolvedImportResolutions(`
+ import * as a from "foo/a/a";
+ import * as b from "foo/a/b";
+ import * as c from "foo/a/c";
+ `, ["foo"], [fooAA, fooAB, fooAC]);
+ });
+ });
}
diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts
index 16267a092fc..698f99616ca 100644
--- a/src/harness/virtualFileSystem.ts
+++ b/src/harness/virtualFileSystem.ts
@@ -125,7 +125,7 @@ namespace Utils {
addFile(path: string, content?: Harness.LanguageService.ScriptInfo) {
const absolutePath = ts.normalizePath(ts.getNormalizedAbsolutePath(path, this.currentDirectory));
- const fileName = ts.getBaseFileName(path);
+ const fileName = ts.getBaseFileName(absolutePath);
const directoryPath = ts.getDirectoryPath(absolutePath);
const directory = this.addDirectory(directoryPath);
return directory ? directory.addFile(fileName, content) : undefined;
diff --git a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl
index db17f37967b..0ef1aeed0cc 100644
--- a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl
+++ b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl
@@ -3906,6 +3906,15 @@
+ -
+
+
+
+
+
+
+
+
-
@@ -6018,6 +6027,9 @@
-
+
+
+
diff --git a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl
index 58326d7ccb6..b2ffdf585e0 100644
--- a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl
+++ b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl
@@ -3894,6 +3894,15 @@
+ -
+
+
+
+
+
+
+
+
-
@@ -6003,6 +6012,9 @@
-
+
+
+
diff --git a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl
index 342918edc19..259d4b3b90e 100644
--- a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl
+++ b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl
@@ -3906,6 +3906,15 @@
+ -
+
+
+
+
+
+
+
+
-
@@ -6018,6 +6027,9 @@
-
+
+
+
diff --git a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl
index 52881bd0f4e..d3663b384db 100644
--- a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl
+++ b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl
@@ -3906,6 +3906,15 @@
+ -
+
+
+
+
+
+
+
+
-
@@ -6018,6 +6027,9 @@
-
+
+
+
diff --git a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl
index a0f54926f3f..43d2f167e96 100644
--- a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl
+++ b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl
@@ -3897,6 +3897,15 @@
+ -
+
+
+
+
+
+
+
+
-
@@ -6009,6 +6018,9 @@
-
+
+
+
diff --git a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl
index 4b42ff8b2bf..0e70ff04cca 100644
--- a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl
+++ b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl
@@ -3897,6 +3897,15 @@
+ -
+
+
+
+
+
+
+
+
-
@@ -6009,6 +6018,9 @@
-
+
+
+
diff --git a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl
index 473da1a4a01..3252fe24f15 100644
--- a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl
+++ b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl
@@ -3887,6 +3887,15 @@
+ -
+
+
+
+
+
+
+
+
-
@@ -5996,6 +6005,9 @@
-
+
+
+
diff --git a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl
index b341aa7ea84..07d26979bf8 100644
--- a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl
+++ b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl
@@ -3896,6 +3896,15 @@
+ -
+
+
+
+
+
+
+
+
-
@@ -6008,6 +6017,9 @@
-
+
+
+
diff --git a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl
index 1ac1d8a83e6..72116417c1b 100644
--- a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl
+++ b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl
@@ -3890,6 +3890,15 @@
+ -
+
+
+
+
+
+
+
+
-
@@ -6002,6 +6011,9 @@
-
+
+
+
diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts
index c7903289a76..006c161ce56 100644
--- a/src/server/editorServices.ts
+++ b/src/server/editorServices.ts
@@ -312,7 +312,8 @@ namespace ts.server {
export class ProjectService {
- public readonly typingsCache: TypingsCache;
+ /*@internal*/
+ readonly typingsCache: TypingsCache;
private readonly documentRegistry: DocumentRegistry;
@@ -523,13 +524,13 @@ namespace ts.server {
}
switch (response.kind) {
case ActionSet:
- project.resolutionCache.clear();
- this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typeAcquisition, response.unresolvedImports, response.typings);
+ // Update the typing files and update the project
+ project.updateTypingFiles(this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typeAcquisition, response.unresolvedImports, response.typings));
break;
case ActionInvalidate:
- project.resolutionCache.clear();
- this.typingsCache.deleteTypingsForProject(response.projectName);
- break;
+ // Do not clear resolution cache, there was changes detected in typings, so enque typing request and let it get us correct results
+ this.typingsCache.enqueueInstallTypingsForProject(project, project.lastCachedUnresolvedImportsList, /*forceRefresh*/ true);
+ return;
}
this.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(project);
}
diff --git a/src/server/project.ts b/src/server/project.ts
index 8e9ff1dfc9c..b79d9245898 100644
--- a/src/server/project.ts
+++ b/src/server/project.ts
@@ -55,34 +55,6 @@ namespace ts.server {
projectErrors: ReadonlyArray;
}
- export class UnresolvedImportsMap {
- readonly perFileMap = createMap>();
- private version = 0;
-
- public clear() {
- this.perFileMap.clear();
- this.version = 0;
- }
-
- public getVersion() {
- return this.version;
- }
-
- public remove(path: Path) {
- this.perFileMap.delete(path);
- this.version++;
- }
-
- public get(path: Path) {
- return this.perFileMap.get(path);
- }
-
- public set(path: Path, value: ReadonlyArray) {
- this.perFileMap.set(path, value);
- this.version++;
- }
- }
-
export interface PluginCreateInfo {
project: Project;
languageService: LanguageService;
@@ -116,8 +88,18 @@ namespace ts.server {
private missingFilesMap: Map;
private plugins: PluginModule[] = [];
- private cachedUnresolvedImportsPerFile = new UnresolvedImportsMap();
- private lastCachedUnresolvedImportsList: SortedReadonlyArray;
+ /*@internal*/
+ /**
+ * This is map from files to unresolved imports in it
+ * Maop does not contain entries for files that do not have unresolved imports
+ * This helps in containing the set of files to invalidate
+ */
+ cachedUnresolvedImportsPerFile = createMap>();
+
+ /*@internal*/
+ lastCachedUnresolvedImportsList: SortedReadonlyArray;
+ /*@internal*/
+ private hasAddedorRemovedFiles = false;
private lastFileExceededProgramSize: string | undefined;
@@ -149,10 +131,10 @@ namespace ts.server {
*/
private lastReportedVersion = 0;
/**
- * Current project structure version.
+ * Current project's program version. (incremented everytime new program is created that is not complete reuse from the old one)
* This property is changed in 'updateGraph' based on the set of files in program
*/
- private projectStructureVersion = 0;
+ private projectProgramVersion = 0;
/**
* Current version of the project state. It is changed when:
* - new root file was added/removed
@@ -167,7 +149,8 @@ namespace ts.server {
/*@internal*/
hasChangedAutomaticTypeDirectiveNames = false;
- private typingFiles: SortedReadonlyArray;
+ /*@internal*/
+ typingFiles: SortedReadonlyArray = emptyArray;
private readonly cancellationToken: ThrottledCancellationToken;
@@ -181,10 +164,6 @@ namespace ts.server {
return hasOneOrMoreJsAndNoTsFiles(this);
}
- public getCachedUnresolvedImportsPerFile_TestOnly() {
- return this.cachedUnresolvedImportsPerFile;
- }
-
public static resolveModule(moduleName: string, initialDir: string, host: ServerHost, log: (message: string) => void): {} {
const resolvedPath = normalizeSlashes(host.resolvePath(combinePaths(initialDir, "node_modules")));
log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`);
@@ -742,7 +721,7 @@ namespace ts.server {
else {
this.resolutionCache.invalidateResolutionOfFile(info.path);
}
- this.cachedUnresolvedImportsPerFile.remove(info.path);
+ this.cachedUnresolvedImportsPerFile.delete(info.path);
if (detachFromProject) {
info.detachFromProject(this);
@@ -763,16 +742,13 @@ namespace ts.server {
}
/* @internal */
- private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: Push, ambientModules: string[]) {
+ private extractUnresolvedImportsFromSourceFile(file: SourceFile, ambientModules: string[]): ReadonlyArray {
const cached = this.cachedUnresolvedImportsPerFile.get(file.path);
if (cached) {
- // found cached result - use it and return
- for (const f of cached) {
- result.push(f);
- }
- return;
+ // found cached result, return
+ return cached;
}
- let unresolvedImports: string[];
+ let unresolvedImports: string[] | undefined;
if (file.resolvedModules) {
file.resolvedModules.forEach((resolvedModule, name) => {
// pick unresolved non-relative names
@@ -788,17 +764,23 @@ namespace ts.server {
trimmed = trimmed.substr(0, i);
}
(unresolvedImports || (unresolvedImports = [])).push(trimmed);
- result.push(trimmed);
}
});
}
+
this.cachedUnresolvedImportsPerFile.set(file.path, unresolvedImports || emptyArray);
+ return unresolvedImports || emptyArray;
function isAmbientlyDeclaredModule(name: string) {
return ambientModules.some(m => m === name);
}
}
+ /* @internal */
+ onFileAddedOrRemoved() {
+ this.hasAddedorRemovedFiles = true;
+ }
+
/**
* Updates set of files that contribute to this project
* @returns: true if set of files in the project stays the same and false - otherwise.
@@ -806,13 +788,15 @@ namespace ts.server {
updateGraph(): boolean {
this.resolutionCache.startRecordingFilesWithChangedResolutions();
- let hasChanges = this.updateGraphWorker();
+ const hasNewProgram = this.updateGraphWorker();
+ const hasAddedorRemovedFiles = this.hasAddedorRemovedFiles;
+ this.hasAddedorRemovedFiles = false;
const changedFiles: ReadonlyArray = this.resolutionCache.finishRecordingFilesWithChangedResolutions() || emptyArray;
for (const file of changedFiles) {
// delete cached information for changed files
- this.cachedUnresolvedImportsPerFile.remove(file);
+ this.cachedUnresolvedImportsPerFile.delete(file);
}
// update builder only if language service is enabled
@@ -824,30 +808,35 @@ namespace ts.server {
// 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files
// (can reuse cached imports for files that were not changed)
// 4. compilation settings were changed in the way that might affect module resolution - drop all caches and collect all data from the scratch
- if (hasChanges || changedFiles.length) {
- const result: string[] = [];
+ if (hasNewProgram || changedFiles.length) {
+ let result: string[] | undefined;
const ambientModules = this.program.getTypeChecker().getAmbientModules().map(mod => stripQuotes(mod.getName()));
for (const sourceFile of this.program.getSourceFiles()) {
- this.extractUnresolvedImportsFromSourceFile(sourceFile, result, ambientModules);
+ const unResolved = this.extractUnresolvedImportsFromSourceFile(sourceFile, ambientModules);
+ if (unResolved !== emptyArray) {
+ (result || (result = [])).push(...unResolved);
+ }
}
- this.lastCachedUnresolvedImportsList = toDeduplicatedSortedArray(result);
+ this.lastCachedUnresolvedImportsList = result ? toDeduplicatedSortedArray(result) : emptyArray;
}
- const cachedTypings = this.projectService.typingsCache.getTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasChanges);
- if (!arrayIsEqualTo(this.typingFiles, cachedTypings)) {
- this.typingFiles = cachedTypings;
- this.markAsDirty();
- hasChanges = this.updateGraphWorker() || hasChanges;
- }
+ this.projectService.typingsCache.enqueueInstallTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasAddedorRemovedFiles);
}
else {
this.lastCachedUnresolvedImportsList = undefined;
}
- if (hasChanges) {
- this.projectStructureVersion++;
+ if (hasNewProgram) {
+ this.projectProgramVersion++;
}
- return !hasChanges;
+ return !hasNewProgram;
+ }
+
+ /*@internal*/
+ updateTypingFiles(typingFiles: SortedReadonlyArray) {
+ this.typingFiles = typingFiles;
+ // Invalidate files with unresolved imports
+ this.resolutionCache.setFilesWithInvalidatedNonRelativeUnresolvedImports(this.cachedUnresolvedImportsPerFile);
}
/* @internal */
@@ -876,9 +865,9 @@ namespace ts.server {
// bump up the version if
// - oldProgram is not set - this is a first time updateGraph is called
// - newProgram is different from the old program and structure of the old program was not reused.
- const hasChanges = this.program && (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely)));
+ const hasNewProgram = this.program && (!oldProgram || (this.program !== oldProgram && !(oldProgram.structureIsReused & StructureIsReused.Completely)));
this.hasChangedAutomaticTypeDirectiveNames = false;
- if (hasChanges) {
+ if (hasNewProgram) {
if (oldProgram) {
for (const f of oldProgram.getSourceFiles()) {
if (this.program.getSourceFileByPath(f.path)) {
@@ -916,8 +905,8 @@ namespace ts.server {
removed => this.detachScriptInfoFromProject(removed)
);
const elapsed = timestamp() - start;
- this.writeLog(`Finishing updateGraphWorker: Project: ${this.getProjectName()} Version: ${this.getProjectVersion()} structureChanged: ${hasChanges} Elapsed: ${elapsed}ms`);
- return hasChanges;
+ this.writeLog(`Finishing updateGraphWorker: Project: ${this.getProjectName()} Version: ${this.getProjectVersion()} structureChanged: ${hasNewProgram} Elapsed: ${elapsed}ms`);
+ return hasNewProgram;
}
private detachScriptInfoFromProject(uncheckedFileName: string) {
@@ -985,15 +974,13 @@ namespace ts.server {
setCompilerOptions(compilerOptions: CompilerOptions) {
if (compilerOptions) {
compilerOptions.allowNonTsExtensions = true;
- if (changesAffectModuleResolution(this.compilerOptions, compilerOptions)) {
- // reset cached unresolved imports if changes in compiler options affected module resolution
- this.cachedUnresolvedImportsPerFile.clear();
- this.lastCachedUnresolvedImportsList = undefined;
- }
const oldOptions = this.compilerOptions;
this.compilerOptions = compilerOptions;
this.setInternalCompilerOptionsForEmittingJsFiles();
if (changesAffectModuleResolution(oldOptions, compilerOptions)) {
+ // reset cached unresolved imports if changes in compiler options affected module resolution
+ this.cachedUnresolvedImportsPerFile.clear();
+ this.lastCachedUnresolvedImportsList = undefined;
this.resolutionCache.clear();
}
this.markAsDirty();
@@ -1006,7 +993,7 @@ namespace ts.server {
const info: protocol.ProjectVersionInfo = {
projectName: this.getProjectName(),
- version: this.projectStructureVersion,
+ version: this.projectProgramVersion,
isInferred: this.projectKind === ProjectKind.Inferred,
options: this.getCompilationSettings(),
languageServiceDisabled: !this.languageServiceEnabled,
@@ -1017,7 +1004,7 @@ namespace ts.server {
// check if requested version is the same that we have reported last time
if (this.lastReportedFileNames && lastKnownVersion === this.lastReportedVersion) {
// if current structure version is the same - return info without any changes
- if (this.projectStructureVersion === this.lastReportedVersion && !updatedFileNames) {
+ if (this.projectProgramVersion === this.lastReportedVersion && !updatedFileNames) {
return { info, projectErrors: this.getGlobalProjectErrors() };
}
// compute and return the difference
@@ -1040,7 +1027,7 @@ namespace ts.server {
}
});
this.lastReportedFileNames = currentFiles;
- this.lastReportedVersion = this.projectStructureVersion;
+ this.lastReportedVersion = this.projectProgramVersion;
return { info, changes: { added, removed, updated }, projectErrors: this.getGlobalProjectErrors() };
}
else {
@@ -1049,7 +1036,7 @@ namespace ts.server {
const externalFiles = this.getExternalFiles().map(f => toNormalizedPath(f));
const allFiles = projectFileNames.concat(externalFiles);
this.lastReportedFileNames = arrayToSet(allFiles);
- this.lastReportedVersion = this.projectStructureVersion;
+ this.lastReportedVersion = this.projectProgramVersion;
return { info, files: allFiles, projectErrors: this.getGlobalProjectErrors() };
}
}
diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts
index db56973d796..589975769b1 100644
--- a/src/server/scriptInfo.ts
+++ b/src/server/scriptInfo.ts
@@ -304,6 +304,7 @@ namespace ts.server {
const isNew = !this.isAttached(project);
if (isNew) {
this.containingProjects.push(project);
+ project.onFileAddedOrRemoved();
if (!project.getCompilerOptions().preserveSymlinks) {
this.ensureRealPath();
}
@@ -328,19 +329,24 @@ namespace ts.server {
return;
case 1:
if (this.containingProjects[0] === project) {
+ project.onFileAddedOrRemoved();
this.containingProjects.pop();
}
break;
case 2:
if (this.containingProjects[0] === project) {
+ project.onFileAddedOrRemoved();
this.containingProjects[0] = this.containingProjects.pop();
}
else if (this.containingProjects[1] === project) {
+ project.onFileAddedOrRemoved();
this.containingProjects.pop();
}
break;
default:
- unorderedRemoveItem(this.containingProjects, project);
+ if (unorderedRemoveItem(this.containingProjects, project)) {
+ project.onFileAddedOrRemoved();
+ }
break;
}
}
diff --git a/src/server/session.ts b/src/server/session.ts
index 1076d17e11f..27044ec369d 100644
--- a/src/server/session.ts
+++ b/src/server/session.ts
@@ -634,7 +634,8 @@ namespace ts.server {
code: d.code,
source: d.source,
startLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start),
- endLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start + d.length)
+ endLocation: scriptInfo && scriptInfo.positionToLineOffset(d.start + d.length),
+ reportsUnnecessary: d.reportsUnnecessary
});
}
diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json
index fe19cd418bd..dc732604ab6 100644
--- a/src/server/tsconfig.json
+++ b/src/server/tsconfig.json
@@ -108,10 +108,8 @@
"../services/codefixes/fixInvalidImportSyntax.ts",
"../services/codefixes/fixStrictClassInitialization.ts",
"../services/codefixes/useDefaultImport.ts",
- "../services/codefixes/fixes.ts",
"../services/refactors/extractSymbol.ts",
"../services/refactors/generateGetAccessorAndSetAccessor.ts",
- "../services/refactors/refactors.ts",
"../services/sourcemaps.ts",
"../services/services.ts",
"../services/breakpoints.ts",
diff --git a/src/server/tsconfig.library.json b/src/server/tsconfig.library.json
index 54353daf5c6..dd0196fab76 100644
--- a/src/server/tsconfig.library.json
+++ b/src/server/tsconfig.library.json
@@ -114,10 +114,8 @@
"../services/codefixes/fixInvalidImportSyntax.ts",
"../services/codefixes/fixStrictClassInitialization.ts",
"../services/codefixes/useDefaultImport.ts",
- "../services/codefixes/fixes.ts",
"../services/refactors/extractSymbol.ts",
"../services/refactors/generateGetAccessorAndSetAccessor.ts",
- "../services/refactors/refactors.ts",
"../services/sourcemaps.ts",
"../services/services.ts",
"../services/breakpoints.ts",
diff --git a/src/server/typingsCache.ts b/src/server/typingsCache.ts
index f2642230f2d..c255757481f 100644
--- a/src/server/typingsCache.ts
+++ b/src/server/typingsCache.ts
@@ -24,7 +24,7 @@ namespace ts.server {
globalTypingsCacheLocation: undefined
};
- class TypingsCacheEntry {
+ interface TypingsCacheEntry {
readonly typeAcquisition: TypeAcquisition;
readonly compilerOptions: CompilerOptions;
readonly typings: SortedReadonlyArray;
@@ -80,6 +80,7 @@ namespace ts.server {
return !arrayIsEqualTo(imports1, imports2);
}
+ /*@internal*/
export class TypingsCache {
private readonly perProjectCache: Map = createMap();
@@ -94,15 +95,14 @@ namespace ts.server {
return this.installer.installPackage(options);
}
- getTypingsForProject(project: Project, unresolvedImports: SortedReadonlyArray, forceRefresh: boolean): SortedReadonlyArray {
+ enqueueInstallTypingsForProject(project: Project, unresolvedImports: SortedReadonlyArray, forceRefresh: boolean) {
const typeAcquisition = project.getTypeAcquisition();
if (!typeAcquisition || !typeAcquisition.enable) {
- return emptyArray;
+ return;
}
const entry = this.perProjectCache.get(project.getProjectName());
- const result: SortedReadonlyArray = entry ? entry.typings : emptyArray;
if (forceRefresh ||
!entry ||
typeAcquisitionChanged(typeAcquisition, entry.typeAcquisition) ||
@@ -113,28 +113,25 @@ namespace ts.server {
this.perProjectCache.set(project.getProjectName(), {
compilerOptions: project.getCompilationSettings(),
typeAcquisition,
- typings: result,
+ typings: entry ? entry.typings : emptyArray,
unresolvedImports,
poisoned: true
});
// something has been changed, issue a request to update typings
this.installer.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
}
- return result;
}
updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray, newTypings: string[]) {
+ const typings = toSortedArray(newTypings);
this.perProjectCache.set(projectName, {
compilerOptions,
typeAcquisition,
- typings: toSortedArray(newTypings),
+ typings,
unresolvedImports,
poisoned: false
});
- }
-
- deleteTypingsForProject(projectName: string) {
- this.perProjectCache.delete(projectName);
+ return !typeAcquisition || !typeAcquisition.enable ? emptyArray : typings;
}
onProjectClosed(project: Project) {
diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts
index 8d482241d1b..f79564f6f98 100644
--- a/src/server/typingsInstaller/typingsInstaller.ts
+++ b/src/server/typingsInstaller/typingsInstaller.ts
@@ -64,11 +64,13 @@ namespace ts.server.typingsInstaller {
onRequestCompleted: RequestCompletedAction;
}
+ type ProjectWatchers = Map & { isInvoked?: boolean; };
+
export abstract class TypingsInstaller {
private readonly packageNameToTypingLocation: Map = createMap();
private readonly missingTypingsSet: Map = createMap();
private readonly knownCachesSet: Map = createMap();
- private readonly projectWatchers = createMap