mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-26 21:23:53 -06:00
CR feedback
This commit is contained in:
parent
9ed5b4c435
commit
002f0c066b
@ -389,7 +389,7 @@ namespace ts {
|
||||
catch (e) {
|
||||
return { error: createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message) };
|
||||
}
|
||||
return parseConfigFileText(fileName, text);
|
||||
return parseConfigFileTextToJson(fileName, text);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -397,7 +397,7 @@ namespace ts {
|
||||
* @param fileName The path to the config file
|
||||
* @param jsonText The text of the config file
|
||||
*/
|
||||
export function parseConfigFileText(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
|
||||
export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
|
||||
try {
|
||||
return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} };
|
||||
}
|
||||
@ -412,7 +412,7 @@ namespace ts {
|
||||
* @param basePath A root directory to resolve relative path entries in the config
|
||||
* file to. e.g. outDir
|
||||
*/
|
||||
export function parseConfigFile(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine {
|
||||
export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine {
|
||||
let errors: Diagnostic[] = [];
|
||||
|
||||
return {
|
||||
|
||||
@ -833,24 +833,15 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function doTwoArraysHaveTheSameElements<T>(array1: Array<T>, array2: Array<T>): Boolean {
|
||||
export function arrayStructurallyIsEqualTo<T>(array1: Array<T>, array2: Array<T>): boolean {
|
||||
if (!array1 || !array2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array1.length != array2.length) {
|
||||
if (array1.length !== array2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
array1 = array1.sort();
|
||||
array2 = array2.sort();
|
||||
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
if (array1[i] != array2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return arrayIsEqualTo(array1.sort(), array2.sort());
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,32 +199,19 @@ namespace ts {
|
||||
const _path = require("path");
|
||||
const _os = require("os");
|
||||
|
||||
class WatchedFileSet {
|
||||
private watchedFiles: WatchedFile[] = [];
|
||||
private nextFileToCheck = 0;
|
||||
private watchTimer: any;
|
||||
// average async stat takes about 30 microseconds
|
||||
// set chunk size to do 30 files in < 1 millisecond
|
||||
function createWatchedFileSet(interval = 2500, chunkSize = 30) {
|
||||
let watchedFiles: WatchedFile[] = [];
|
||||
let nextFileToCheck = 0;
|
||||
let watchTimer: any;
|
||||
|
||||
// average async stat takes about 30 microseconds
|
||||
// set chunk size to do 30 files in < 1 millisecond
|
||||
constructor(public interval = 2500, public chunkSize = 30) {
|
||||
}
|
||||
|
||||
private static copyListRemovingItem<T>(item: T, list: T[]) {
|
||||
let copiedList: T[] = [];
|
||||
for (var i = 0, len = list.length; i < len; i++) {
|
||||
if (list[i] != item) {
|
||||
copiedList.push(list[i]);
|
||||
}
|
||||
}
|
||||
return copiedList;
|
||||
}
|
||||
|
||||
private static getModifiedTime(fileName: string): Date {
|
||||
function getModifiedTime(fileName: string): Date {
|
||||
return _fs.statSync(fileName).mtime;
|
||||
}
|
||||
|
||||
private poll(checkedIndex: number) {
|
||||
let watchedFile = this.watchedFiles[checkedIndex];
|
||||
function poll(checkedIndex: number) {
|
||||
let watchedFile = watchedFiles[checkedIndex];
|
||||
if (!watchedFile) {
|
||||
return;
|
||||
}
|
||||
@ -234,7 +221,7 @@ namespace ts {
|
||||
watchedFile.callback(watchedFile.fileName);
|
||||
}
|
||||
else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) {
|
||||
watchedFile.mtime = WatchedFileSet.getModifiedTime(watchedFile.fileName);
|
||||
watchedFile.mtime = getModifiedTime(watchedFile.fileName);
|
||||
watchedFile.callback(watchedFile.fileName, watchedFile.mtime.getTime() === 0);
|
||||
}
|
||||
});
|
||||
@ -243,42 +230,50 @@ namespace ts {
|
||||
// this implementation uses polling and
|
||||
// stat due to inconsistencies of fs.watch
|
||||
// and efficiency of stat on modern filesystems
|
||||
private startWatchTimer() {
|
||||
this.watchTimer = setInterval(() => {
|
||||
function startWatchTimer() {
|
||||
watchTimer = setInterval(() => {
|
||||
let count = 0;
|
||||
let nextToCheck = this.nextFileToCheck;
|
||||
let nextToCheck = nextFileToCheck;
|
||||
let firstCheck = -1;
|
||||
while ((count < this.chunkSize) && (nextToCheck !== firstCheck)) {
|
||||
this.poll(nextToCheck);
|
||||
while ((count < chunkSize) && (nextToCheck !== firstCheck)) {
|
||||
poll(nextToCheck);
|
||||
if (firstCheck < 0) {
|
||||
firstCheck = nextToCheck;
|
||||
}
|
||||
nextToCheck++;
|
||||
if (nextToCheck === this.watchedFiles.length) {
|
||||
if (nextToCheck === watchedFiles.length) {
|
||||
nextToCheck = 0;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
this.nextFileToCheck = nextToCheck;
|
||||
}, this.interval);
|
||||
nextFileToCheck = nextToCheck;
|
||||
}, interval);
|
||||
}
|
||||
|
||||
addFile(fileName: string, callback: (fileName: string, removed?: boolean) => void): WatchedFile {
|
||||
function addFile(fileName: string, callback: (fileName: string, removed?: boolean) => void): WatchedFile {
|
||||
let file: WatchedFile = {
|
||||
fileName,
|
||||
callback,
|
||||
mtime: WatchedFileSet.getModifiedTime(fileName)
|
||||
mtime: getModifiedTime(fileName)
|
||||
};
|
||||
|
||||
this.watchedFiles.push(file);
|
||||
if (this.watchedFiles.length === 1) {
|
||||
this.startWatchTimer();
|
||||
watchedFiles.push(file);
|
||||
if (watchedFiles.length === 1) {
|
||||
startWatchTimer();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
removeFile(file: WatchedFile) {
|
||||
this.watchedFiles = WatchedFileSet.copyListRemovingItem(file, this.watchedFiles);
|
||||
function removeFile(file: WatchedFile) {
|
||||
watchedFiles = copyListRemovingItem(file, watchedFiles);
|
||||
}
|
||||
|
||||
return {
|
||||
getModifiedTime: getModifiedTime,
|
||||
poll: poll,
|
||||
startWatchTimer: startWatchTimer,
|
||||
addFile: addFile,
|
||||
removeFile: removeFile
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,7 +290,7 @@ namespace ts {
|
||||
// changes for large reference sets? If so, do we want
|
||||
// to increase the chunk size or decrease the interval
|
||||
// time dynamically to match the large reference set?
|
||||
let watchedFileSet = new WatchedFileSet();
|
||||
let watchedFileSet = createWatchedFileSet();
|
||||
|
||||
function isNode4OrLater(): Boolean {
|
||||
return parseInt(process.version.charAt(1)) >= 4;
|
||||
@ -417,7 +412,7 @@ namespace ts {
|
||||
// In watchDirectory we only care about adding and removing files (when event name is
|
||||
// "rename"); changes made within files are handled by corresponding fileWatchers (when
|
||||
// event name is "change")
|
||||
if (eventName == "rename") {
|
||||
if (eventName === "rename") {
|
||||
// When deleting a file, the passed baseFileName is null
|
||||
callback(!relativeFileName ? relativeFileName : normalizePath(ts.combinePaths(path, relativeFileName)));
|
||||
};
|
||||
|
||||
@ -147,15 +147,17 @@ namespace ts {
|
||||
|
||||
export function executeCommandLine(args: string[]): void {
|
||||
let commandLine = parseCommandLine(args);
|
||||
let configFileName: string; // Configuration file name (if any)
|
||||
let configFileWatcher: FileWatcher; // Configuration file watcher
|
||||
let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal
|
||||
let cachedProgram: Program; // Program cached from last compilation
|
||||
let rootFileNames: string[]; // Root fileNames for compilation
|
||||
let compilerOptions: CompilerOptions; // Compiler options for compilation
|
||||
let compilerHost: CompilerHost; // Compiler host
|
||||
let hostGetSourceFile: typeof compilerHost.getSourceFile; // getSourceFile method from default host
|
||||
let timerHandle: number; // Handle for 0.25s wait timer
|
||||
let configFileName: string; // Configuration file name (if any)
|
||||
let cachedConfigFileText: string; // Cached configuration file text, used for reparsing (if any)
|
||||
let configFileWatcher: FileWatcher; // Configuration file watcher
|
||||
let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal
|
||||
let cachedProgram: Program; // Program cached from last compilation
|
||||
let rootFileNames: string[]; // Root fileNames for compilation
|
||||
let compilerOptions: CompilerOptions; // Compiler options for compilation
|
||||
let compilerHost: CompilerHost; // Compiler host
|
||||
let hostGetSourceFile: typeof compilerHost.getSourceFile; // getSourceFile method from default host
|
||||
let timerHandleForRecompilation: number; // Handle for 0.25s wait timer to trigger recompilation
|
||||
let timerHandleForDirectoryChanges: number; // Handle for 0.25s wait timer to trigger directory change handler
|
||||
|
||||
if (commandLine.options.locale) {
|
||||
if (!isJSONSupported()) {
|
||||
@ -232,16 +234,22 @@ namespace ts {
|
||||
|
||||
performCompilation();
|
||||
|
||||
function configFileToParsedCommandLine(configFilename: string): ParsedCommandLine {
|
||||
let result = readConfigFile(configFileName, sys.readFile);
|
||||
if (result.error) {
|
||||
reportWatchDiagnostic(result.error);
|
||||
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
return;
|
||||
function parseConfigFile(): ParsedCommandLine {
|
||||
if (!cachedConfigFileText) {
|
||||
try {
|
||||
cachedConfigFileText = sys.readFile(configFileName);
|
||||
}
|
||||
catch (e) {
|
||||
let error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message);
|
||||
reportWatchDiagnostic(error);
|
||||
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let result = parseConfigFileTextToJson(configFileName, cachedConfigFileText);
|
||||
let configObject = result.config;
|
||||
let configParseResult = parseConfigFile(configObject, sys, getDirectoryPath(configFileName));
|
||||
let configParseResult = parseJsonConfigFileContent(configObject, sys, getDirectoryPath(configFileName));
|
||||
if (configParseResult.errors.length > 0) {
|
||||
reportDiagnostics(configParseResult.errors);
|
||||
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
@ -255,7 +263,7 @@ namespace ts {
|
||||
|
||||
if (!cachedProgram) {
|
||||
if (configFileName) {
|
||||
let configParseResult = configFileToParsedCommandLine(configFileName);
|
||||
let configParseResult = parseConfigFile();
|
||||
rootFileNames = configParseResult.fileNames;
|
||||
compilerOptions = extend(commandLine.options, configParseResult.options);
|
||||
}
|
||||
@ -322,13 +330,14 @@ namespace ts {
|
||||
rootFileNames.splice(index, 1);
|
||||
}
|
||||
}
|
||||
startTimer();
|
||||
startTimerForRecompilation();
|
||||
}
|
||||
|
||||
// If the configuration file changes, forget cached program and start the recompilation timer
|
||||
function configFileChanged() {
|
||||
setCachedProgram(undefined);
|
||||
startTimer();
|
||||
cachedConfigFileText = undefined;
|
||||
startTimerForRecompilation();
|
||||
}
|
||||
|
||||
function watchedDirectoryChanged(fileName: string) {
|
||||
@ -336,28 +345,39 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
|
||||
let parsedCommandLine = configFileToParsedCommandLine(configFileName);
|
||||
let newFileNames = parsedCommandLine.fileNames.map(compilerHost.getCanonicalFileName);
|
||||
let canonicalRootFileNames = rootFileNames.map(compilerHost.getCanonicalFileName);
|
||||
startTimerForHandlingDirectoryChanges();
|
||||
}
|
||||
|
||||
if (!doTwoArraysHaveTheSameElements(newFileNames, canonicalRootFileNames)) {
|
||||
function startTimerForHandlingDirectoryChanges() {
|
||||
if (timerHandleForDirectoryChanges) {
|
||||
clearTimeout(timerHandleForDirectoryChanges);
|
||||
}
|
||||
timerHandleForDirectoryChanges = setTimeout(directoryChangeHandler, 250);
|
||||
}
|
||||
|
||||
function directoryChangeHandler() {
|
||||
let parsedCommandLine = parseConfigFile();
|
||||
let newFileNames = ts.map(parsedCommandLine.fileNames, compilerHost.getCanonicalFileName);
|
||||
let canonicalRootFileNames = ts.map(rootFileNames, compilerHost.getCanonicalFileName);
|
||||
|
||||
if (!arrayStructurallyIsEqualTo(newFileNames, canonicalRootFileNames)) {
|
||||
setCachedProgram(undefined);
|
||||
startTimer();
|
||||
startTimerForRecompilation();
|
||||
}
|
||||
}
|
||||
|
||||
// Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch
|
||||
// operations (such as saving all modified files in an editor) a chance to complete before we kick
|
||||
// off a new compilation.
|
||||
function startTimer() {
|
||||
if (timerHandle) {
|
||||
clearTimeout(timerHandle);
|
||||
function startTimerForRecompilation() {
|
||||
if (timerHandleForRecompilation) {
|
||||
clearTimeout(timerHandleForRecompilation);
|
||||
}
|
||||
timerHandle = setTimeout(recompile, 250);
|
||||
timerHandleForRecompilation = setTimeout(recompile, 250);
|
||||
}
|
||||
|
||||
function recompile() {
|
||||
timerHandle = undefined;
|
||||
timerHandleForRecompilation = undefined;
|
||||
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation));
|
||||
performCompilation();
|
||||
}
|
||||
|
||||
@ -2406,4 +2406,14 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function copyListRemovingItem<T>(item: T, list: T[]) {
|
||||
var copiedList: T[] = [];
|
||||
for (var i = 0, len = list.length; i < len; i++) {
|
||||
if (list[i] != item) {
|
||||
copiedList.push(list[i]);
|
||||
}
|
||||
}
|
||||
return copiedList;
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,12 +556,11 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
this.log("Detected source file changes: " + fileName);
|
||||
|
||||
let { succeeded, projectOptions, error } = this.configFileToProjectOptions(project.projectFilename);
|
||||
let newRootFiles = projectOptions.files.map(f => this.getCanonicalFileName(f));
|
||||
let currentRootFiles = project.getRootFiles().map(f => this.getCanonicalFileName(f));
|
||||
let newRootFiles = ts.map(projectOptions.files, this.getCanonicalFileName);
|
||||
let currentRootFiles = ts.map(project.getRootFiles(), this.getCanonicalFileName);
|
||||
|
||||
if (!doTwoArraysHaveTheSameElements(currentRootFiles, newRootFiles)) {
|
||||
if (!arrayStructurallyIsEqualTo(currentRootFiles, newRootFiles)) {
|
||||
// For configured projects, the change is made outside the tsconfig file, and
|
||||
// it is not likely to affect the project for other files opened by the client. We can
|
||||
// just update the current project.
|
||||
@ -1159,12 +1158,12 @@ namespace ts.server {
|
||||
// file references will be relative to dirPath (or absolute)
|
||||
var dirPath = ts.getDirectoryPath(configFilename);
|
||||
var contents = this.host.readFile(configFilename)
|
||||
var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileText(configFilename, contents);
|
||||
var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.parseConfigFileTextToJson(configFilename, contents);
|
||||
if (rawConfig.error) {
|
||||
return { succeeded: false, error: rawConfig.error };
|
||||
}
|
||||
else {
|
||||
var parsedCommandLine = ts.parseConfigFile(rawConfig.config, this.host, dirPath);
|
||||
var parsedCommandLine = ts.parseJsonConfigFileContent(rawConfig.config, this.host, dirPath);
|
||||
if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) {
|
||||
return { succeeded: false, error: { errorMsg: "tsconfig option errors" } };
|
||||
}
|
||||
|
||||
@ -990,7 +990,7 @@ namespace ts {
|
||||
() => {
|
||||
let text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength());
|
||||
|
||||
let result = parseConfigFileText(fileName, text);
|
||||
let result = parseConfigFileTextToJson(fileName, text);
|
||||
|
||||
if (result.error) {
|
||||
return {
|
||||
@ -1000,7 +1000,7 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
var configFile = parseConfigFile(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName)));
|
||||
var configFile = parseJsonConfigFileContent(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName)));
|
||||
|
||||
return {
|
||||
options: configFile.options,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user