Skip to content

Commit 002f0c0

Browse files
committed
CR feedback
1 parent 9ed5b4c commit 002f0c0

File tree

7 files changed

+108
-93
lines changed

7 files changed

+108
-93
lines changed

src/compiler/commandLineParser.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -389,15 +389,15 @@ namespace ts {
389389
catch (e) {
390390
return { error: createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message) };
391391
}
392-
return parseConfigFileText(fileName, text);
392+
return parseConfigFileTextToJson(fileName, text);
393393
}
394394

395395
/**
396396
* Parse the text of the tsconfig.json file
397397
* @param fileName The path to the config file
398398
* @param jsonText The text of the config file
399399
*/
400-
export function parseConfigFileText(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
400+
export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
401401
try {
402402
return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} };
403403
}
@@ -412,7 +412,7 @@ namespace ts {
412412
* @param basePath A root directory to resolve relative path entries in the config
413413
* file to. e.g. outDir
414414
*/
415-
export function parseConfigFile(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine {
415+
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine {
416416
let errors: Diagnostic[] = [];
417417

418418
return {

src/compiler/core.ts

+3-12
Original file line numberDiff line numberDiff line change
@@ -833,24 +833,15 @@ namespace ts {
833833
}
834834
}
835835

836-
export function doTwoArraysHaveTheSameElements<T>(array1: Array<T>, array2: Array<T>): Boolean {
836+
export function arrayStructurallyIsEqualTo<T>(array1: Array<T>, array2: Array<T>): boolean {
837837
if (!array1 || !array2) {
838838
return false;
839839
}
840840

841-
if (array1.length != array2.length) {
841+
if (array1.length !== array2.length) {
842842
return false;
843843
}
844844

845-
array1 = array1.sort();
846-
array2 = array2.sort();
847-
848-
for (let i = 0; i < array1.length; i++) {
849-
if (array1[i] != array2[i]) {
850-
return false;
851-
}
852-
}
853-
854-
return true;
845+
return arrayIsEqualTo(array1.sort(), array2.sort());
855846
}
856847
}

src/compiler/sys.ts

+36-41
Original file line numberDiff line numberDiff line change
@@ -199,32 +199,19 @@ namespace ts {
199199
const _path = require("path");
200200
const _os = require("os");
201201

202-
class WatchedFileSet {
203-
private watchedFiles: WatchedFile[] = [];
204-
private nextFileToCheck = 0;
205-
private watchTimer: any;
206-
207-
// average async stat takes about 30 microseconds
208-
// set chunk size to do 30 files in < 1 millisecond
209-
constructor(public interval = 2500, public chunkSize = 30) {
210-
}
211-
212-
private static copyListRemovingItem<T>(item: T, list: T[]) {
213-
let copiedList: T[] = [];
214-
for (var i = 0, len = list.length; i < len; i++) {
215-
if (list[i] != item) {
216-
copiedList.push(list[i]);
217-
}
218-
}
219-
return copiedList;
220-
}
221-
222-
private static getModifiedTime(fileName: string): Date {
202+
// average async stat takes about 30 microseconds
203+
// set chunk size to do 30 files in < 1 millisecond
204+
function createWatchedFileSet(interval = 2500, chunkSize = 30) {
205+
let watchedFiles: WatchedFile[] = [];
206+
let nextFileToCheck = 0;
207+
let watchTimer: any;
208+
209+
function getModifiedTime(fileName: string): Date {
223210
return _fs.statSync(fileName).mtime;
224211
}
225212

226-
private poll(checkedIndex: number) {
227-
let watchedFile = this.watchedFiles[checkedIndex];
213+
function poll(checkedIndex: number) {
214+
let watchedFile = watchedFiles[checkedIndex];
228215
if (!watchedFile) {
229216
return;
230217
}
@@ -234,7 +221,7 @@ namespace ts {
234221
watchedFile.callback(watchedFile.fileName);
235222
}
236223
else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) {
237-
watchedFile.mtime = WatchedFileSet.getModifiedTime(watchedFile.fileName);
224+
watchedFile.mtime = getModifiedTime(watchedFile.fileName);
238225
watchedFile.callback(watchedFile.fileName, watchedFile.mtime.getTime() === 0);
239226
}
240227
});
@@ -243,42 +230,50 @@ namespace ts {
243230
// this implementation uses polling and
244231
// stat due to inconsistencies of fs.watch
245232
// and efficiency of stat on modern filesystems
246-
private startWatchTimer() {
247-
this.watchTimer = setInterval(() => {
233+
function startWatchTimer() {
234+
watchTimer = setInterval(() => {
248235
let count = 0;
249-
let nextToCheck = this.nextFileToCheck;
236+
let nextToCheck = nextFileToCheck;
250237
let firstCheck = -1;
251-
while ((count < this.chunkSize) && (nextToCheck !== firstCheck)) {
252-
this.poll(nextToCheck);
238+
while ((count < chunkSize) && (nextToCheck !== firstCheck)) {
239+
poll(nextToCheck);
253240
if (firstCheck < 0) {
254241
firstCheck = nextToCheck;
255242
}
256243
nextToCheck++;
257-
if (nextToCheck === this.watchedFiles.length) {
244+
if (nextToCheck === watchedFiles.length) {
258245
nextToCheck = 0;
259246
}
260247
count++;
261248
}
262-
this.nextFileToCheck = nextToCheck;
263-
}, this.interval);
249+
nextFileToCheck = nextToCheck;
250+
}, interval);
264251
}
265252

266-
addFile(fileName: string, callback: (fileName: string, removed?: boolean) => void): WatchedFile {
253+
function addFile(fileName: string, callback: (fileName: string, removed?: boolean) => void): WatchedFile {
267254
let file: WatchedFile = {
268255
fileName,
269256
callback,
270-
mtime: WatchedFileSet.getModifiedTime(fileName)
257+
mtime: getModifiedTime(fileName)
271258
};
272259

273-
this.watchedFiles.push(file);
274-
if (this.watchedFiles.length === 1) {
275-
this.startWatchTimer();
260+
watchedFiles.push(file);
261+
if (watchedFiles.length === 1) {
262+
startWatchTimer();
276263
}
277264
return file;
278265
}
279266

280-
removeFile(file: WatchedFile) {
281-
this.watchedFiles = WatchedFileSet.copyListRemovingItem(file, this.watchedFiles);
267+
function removeFile(file: WatchedFile) {
268+
watchedFiles = copyListRemovingItem(file, watchedFiles);
269+
}
270+
271+
return {
272+
getModifiedTime: getModifiedTime,
273+
poll: poll,
274+
startWatchTimer: startWatchTimer,
275+
addFile: addFile,
276+
removeFile: removeFile
282277
}
283278
}
284279

@@ -295,7 +290,7 @@ namespace ts {
295290
// changes for large reference sets? If so, do we want
296291
// to increase the chunk size or decrease the interval
297292
// time dynamically to match the large reference set?
298-
let watchedFileSet = new WatchedFileSet();
293+
let watchedFileSet = createWatchedFileSet();
299294

300295
function isNode4OrLater(): Boolean {
301296
return parseInt(process.version.charAt(1)) >= 4;
@@ -417,7 +412,7 @@ namespace ts {
417412
// In watchDirectory we only care about adding and removing files (when event name is
418413
// "rename"); changes made within files are handled by corresponding fileWatchers (when
419414
// event name is "change")
420-
if (eventName == "rename") {
415+
if (eventName === "rename") {
421416
// When deleting a file, the passed baseFileName is null
422417
callback(!relativeFileName ? relativeFileName : normalizePath(ts.combinePaths(path, relativeFileName)));
423418
};

src/compiler/tsc.ts

+49-29
Original file line numberDiff line numberDiff line change
@@ -147,15 +147,17 @@ namespace ts {
147147

148148
export function executeCommandLine(args: string[]): void {
149149
let commandLine = parseCommandLine(args);
150-
let configFileName: string; // Configuration file name (if any)
151-
let configFileWatcher: FileWatcher; // Configuration file watcher
152-
let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal
153-
let cachedProgram: Program; // Program cached from last compilation
154-
let rootFileNames: string[]; // Root fileNames for compilation
155-
let compilerOptions: CompilerOptions; // Compiler options for compilation
156-
let compilerHost: CompilerHost; // Compiler host
157-
let hostGetSourceFile: typeof compilerHost.getSourceFile; // getSourceFile method from default host
158-
let timerHandle: number; // Handle for 0.25s wait timer
150+
let configFileName: string; // Configuration file name (if any)
151+
let cachedConfigFileText: string; // Cached configuration file text, used for reparsing (if any)
152+
let configFileWatcher: FileWatcher; // Configuration file watcher
153+
let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal
154+
let cachedProgram: Program; // Program cached from last compilation
155+
let rootFileNames: string[]; // Root fileNames for compilation
156+
let compilerOptions: CompilerOptions; // Compiler options for compilation
157+
let compilerHost: CompilerHost; // Compiler host
158+
let hostGetSourceFile: typeof compilerHost.getSourceFile; // getSourceFile method from default host
159+
let timerHandleForRecompilation: number; // Handle for 0.25s wait timer to trigger recompilation
160+
let timerHandleForDirectoryChanges: number; // Handle for 0.25s wait timer to trigger directory change handler
159161

160162
if (commandLine.options.locale) {
161163
if (!isJSONSupported()) {
@@ -232,16 +234,22 @@ namespace ts {
232234

233235
performCompilation();
234236

235-
function configFileToParsedCommandLine(configFilename: string): ParsedCommandLine {
236-
let result = readConfigFile(configFileName, sys.readFile);
237-
if (result.error) {
238-
reportWatchDiagnostic(result.error);
239-
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
240-
return;
237+
function parseConfigFile(): ParsedCommandLine {
238+
if (!cachedConfigFileText) {
239+
try {
240+
cachedConfigFileText = sys.readFile(configFileName);
241+
}
242+
catch (e) {
243+
let error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message);
244+
reportWatchDiagnostic(error);
245+
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
246+
return;
247+
}
241248
}
242249

250+
let result = parseConfigFileTextToJson(configFileName, cachedConfigFileText);
243251
let configObject = result.config;
244-
let configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName));
252+
let configParseResult = parseJsonConfigFileContent(configObject, sys, getDirectoryPath(configFileName));
245253
if (configParseResult.errors.length > 0) {
246254
reportDiagnostics(configParseResult.errors);
247255
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
@@ -255,7 +263,7 @@ namespace ts {
255263

256264
if (!cachedProgram) {
257265
if (configFileName) {
258-
let configParseResult = configFileToParsedCommandLine(configFileName);
266+
let configParseResult = parseConfigFile();
259267
rootFileNames = configParseResult.fileNames;
260268
compilerOptions = extend(commandLine.options, configParseResult.options);
261269
}
@@ -322,42 +330,54 @@ namespace ts {
322330
rootFileNames.splice(index, 1);
323331
}
324332
}
325-
startTimer();
333+
startTimerForRecompilation();
326334
}
327335

328336
// If the configuration file changes, forget cached program and start the recompilation timer
329337
function configFileChanged() {
330338
setCachedProgram(undefined);
331-
startTimer();
339+
cachedConfigFileText = undefined;
340+
startTimerForRecompilation();
332341
}
333342

334343
function watchedDirectoryChanged(fileName: string) {
335344
if (fileName && !ts.isSupportedSourceFileName(fileName)) {
336345
return;
337346
}
338347

339-
let parsedCommandLine = configFileToParsedCommandLine(configFileName);
340-
let newFileNames = parsedCommandLine.fileNames.map(compilerHost.getCanonicalFileName);
341-
let canonicalRootFileNames = rootFileNames.map(compilerHost.getCanonicalFileName);
348+
startTimerForHandlingDirectoryChanges();
349+
}
350+
351+
function startTimerForHandlingDirectoryChanges() {
352+
if (timerHandleForDirectoryChanges) {
353+
clearTimeout(timerHandleForDirectoryChanges);
354+
}
355+
timerHandleForDirectoryChanges = setTimeout(directoryChangeHandler, 250);
356+
}
357+
358+
function directoryChangeHandler() {
359+
let parsedCommandLine = parseConfigFile();
360+
let newFileNames = ts.map(parsedCommandLine.fileNames, compilerHost.getCanonicalFileName);
361+
let canonicalRootFileNames = ts.map(rootFileNames, compilerHost.getCanonicalFileName);
342362

343-
if (!doTwoArraysHaveTheSameElements(newFileNames, canonicalRootFileNames)) {
363+
if (!arrayStructurallyIsEqualTo(newFileNames, canonicalRootFileNames)) {
344364
setCachedProgram(undefined);
345-
startTimer();
365+
startTimerForRecompilation();
346366
}
347367
}
348368

349369
// Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch
350370
// operations (such as saving all modified files in an editor) a chance to complete before we kick
351371
// off a new compilation.
352-
function startTimer() {
353-
if (timerHandle) {
354-
clearTimeout(timerHandle);
372+
function startTimerForRecompilation() {
373+
if (timerHandleForRecompilation) {
374+
clearTimeout(timerHandleForRecompilation);
355375
}
356-
timerHandle = setTimeout(recompile, 250);
376+
timerHandleForRecompilation = setTimeout(recompile, 250);
357377
}
358378

359379
function recompile() {
360-
timerHandle = undefined;
380+
timerHandleForRecompilation = undefined;
361381
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation));
362382
performCompilation();
363383
}

src/compiler/utilities.ts

+10
Original file line numberDiff line numberDiff line change
@@ -2406,4 +2406,14 @@ namespace ts {
24062406
}
24072407
}
24082408
}
2409+
2410+
export function copyListRemovingItem<T>(item: T, list: T[]) {
2411+
var copiedList: T[] = [];
2412+
for (var i = 0, len = list.length; i < len; i++) {
2413+
if (list[i] != item) {
2414+
copiedList.push(list[i]);
2415+
}
2416+
}
2417+
return copiedList;
2418+
}
24092419
}

src/server/editorServices.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -556,12 +556,11 @@ namespace ts.server {
556556
}
557557

558558
this.log("Detected source file changes: " + fileName);
559-
560559
let { succeeded, projectOptions, error } = this.configFileToProjectOptions(project.projectFilename);
561-
let newRootFiles = projectOptions.files.map(f => this.getCanonicalFileName(f));
562-
let currentRootFiles = project.getRootFiles().map(f => this.getCanonicalFileName(f));
560+
let newRootFiles = ts.map(projectOptions.files, this.getCanonicalFileName);
561+
let currentRootFiles = ts.map(project.getRootFiles(), this.getCanonicalFileName);
563562

564-
if (!doTwoArraysHaveTheSameElements(currentRootFiles, newRootFiles)) {
563+
if (!arrayStructurallyIsEqualTo(currentRootFiles, newRootFiles)) {
565564
// For configured projects, the change is made outside the tsconfig file, and
566565
// it is not likely to affect the project for other files opened by the client. We can
567566
// just update the current project.
@@ -1159,12 +1158,12 @@ namespace ts.server {
11591158
// file references will be relative to dirPath (or absolute)
11601159
var dirPath = ts.getDirectoryPath(configFilename);
11611160
var contents = this.host.readFile(configFilename)
1162-
var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileText(configFilename, contents);
1161+
var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileTextToJson(configFilename, contents);
11631162
if (rawConfig.error) {
11641163
return { succeeded: false, error: rawConfig.error };
11651164
}
11661165
else {
1167-
var parsedCommandLine = ts.parseConfigFile(rawConfig.config, this.host, dirPath);
1166+
var parsedCommandLine = ts.parseJsonConfigFileContent(rawConfig.config, this.host, dirPath);
11681167
if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) {
11691168
return { succeeded: false, error: { errorMsg: "tsconfig option errors" } };
11701169
}

src/services/shims.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ namespace ts {
990990
() => {
991991
let text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength());
992992

993-
let result = parseConfigFileText(fileName, text);
993+
let result = parseConfigFileTextToJson(fileName, text);
994994

995995
if (result.error) {
996996
return {
@@ -1000,7 +1000,7 @@ namespace ts {
10001000
};
10011001
}
10021002

1003-
var configFile = parseConfigFile(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName)));
1003+
var configFile = parseJsonConfigFileContent(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName)));
10041004

10051005
return {
10061006
options: configFile.options,

0 commit comments

Comments
 (0)