alm
Version:
The best IDE for TypeScript
220 lines (219 loc) • 9.02 kB
JavaScript
;
/**
* This module is responsible for reading (with error reporting) the tsconfig.json
*
* - It will emit the relevant information (configFile) for use by the project service if all good
* - It will emit the errors in the configFile or ask to clear them if needed
* - It will emit the available projects
*
* Note: When the app starts the active project is determined by `session.ts`
*/
Object.defineProperty(exports, "__esModule", { value: true });
var events_1 = require("../../common/events");
var utils = require("../../common/utils");
var tsconfig = require("../workers/lang/core/tsconfig");
/** Disk access / session stuff */
var session = require("./session");
var fmc = require("./fileModelCache");
var flm = require("../workers/fileListing/fileListingMaster");
var workingDir = require("./workingDir");
var fsu = require("../utils/fsu");
/**
* Global variables
*/
/**
* The active project name
* Warning: `export`ed only to allow us to check if there is some details on ts crash restart
*/
exports.activeProjectConfigDetails = null;
exports.activeProjectConfigDetailsUpdated = new events_1.TypedEvent();
/** Only if the file is valid will we end up here */
var configFile = null;
exports.configFileUpdated = new events_1.TypedEvent();
exports.projectFilePathsUpdated = new events_1.TypedEvent();
/**
* Allows incremental update of file paths based on *advanced* project analysis
*/
exports.incrementallyAddedFile = function (filePath) {
if (configFile
&& configFile.project
&& configFile.project.files
&& !configFile.project.files.some(function (f) { return f === filePath; })) {
configFile.project.files.push(filePath);
exports.projectFilePathsUpdated.emit({ filePaths: configFile.project.files });
}
};
/**
* Errors in tsconfig.json
*/
var errorsCache_1 = require("../utils/errorsCache");
exports.errorsInTsconfig = new errorsCache_1.ErrorsCache();
function setErrorsInTsconfig(filePath, errors) {
console.log('TSCONFIG: Error', errors[0].message);
exports.errorsInTsconfig.setErrorsByFilePaths([filePath], errors);
}
function clearErrorsInTsconfig(filePath) {
console.log('TSCONFIG: All Good!', filePath);
exports.errorsInTsconfig.clearErrors();
}
/**
* on server start
*/
function start() {
// Keep session on disk in sync
exports.activeProjectConfigDetailsUpdated.on(function (ap) {
if (ap.tsconfigFilePath) {
session.setTsconfigPath(ap.tsconfigFilePath);
}
});
// Helps us sync only once in the beginning
var synced = false;
// Resume session
var ses = session.readDiskSessionsFile();
if (ses.relativePathToTsconfig) {
var tsconfig_1 = workingDir.makeAbsolute(ses.relativePathToTsconfig);
if (fsu.existsSync(tsconfig_1)) {
// Needs to be set so that we watch it even in case of errors
exports.activeProjectConfigDetails = Utils.tsconfigToActiveProjectConfigDetails(tsconfig_1);
// Try and sync with these details
syncCore(exports.activeProjectConfigDetails);
synced = true;
}
}
refreshAvailableProjects()
.then(function () { return !synced && sync(); });
}
exports.start = start;
/** All the available projects */
exports.availableProjects = new events_1.TypedEvent();
function refreshAvailableProjects() {
return flm.filePathsCompleted.current().then(function (list) {
// Detect some tsconfig.json
var tsconfigs = list.filePaths.map(function (t) { return t.filePath; }).filter(function (t) { return t.endsWith('tsconfig.json'); });
// sort by shortest length first (with extra big weight for node_modules):
var weightConfig = function (config) { return config.includes('node_modules') ? config.length + 100 : config.length; };
tsconfigs = tsconfigs.sort(function (a, b) {
return weightConfig(a) - weightConfig(b);
});
var projectConfigs = tsconfigs.map(Utils.tsconfigToActiveProjectConfigDetails);
exports.availableProjects.emit(projectConfigs);
});
}
/** General purpose utility functions specific to this file */
var Utils;
(function (Utils) {
/**
* Used to
* - convert a filePath found in directory indexing into a project that is selectable
* - thaw last session active project filePath
*/
function tsconfigToActiveProjectConfigDetails(filePath) {
var relative = workingDir.makeRelative(filePath);
var isNodeModule = relative.includes('node_modules');
var isVirtual = utils.isJsOrTs(filePath) ? true : false;
return {
name: isNodeModule ? relative : utils.getDirectoryAndFileName(filePath),
isVirtual: isVirtual,
tsconfigFilePath: filePath
};
}
Utils.tsconfigToActiveProjectConfigDetails = tsconfigToActiveProjectConfigDetails;
})(Utils || (Utils = {}));
/** convert project name to current project */
function sync() {
exports.availableProjects.current().then(function (projectConfigs) {
var activeProjectName = (exports.activeProjectConfigDetails && exports.activeProjectConfigDetails.tsconfigFilePath);
var projectConfig = projectConfigs.find(function (x) { return x.tsconfigFilePath == activeProjectName; });
if (!projectConfig) {
console.log('[TSCONFIG]: No active project');
return;
}
syncCore(projectConfig);
});
}
exports.sync = sync;
/** ensures that the `projectConfig` can actually be parsed. If so propogates the set event. */
function syncCore(projectConfig) {
var activeProjectName = (exports.activeProjectConfigDetails && exports.activeProjectConfigDetails.name);
configFile = ConfigFile.getConfigFileFromDiskOrInMemory(projectConfig);
// In case of error we exit as `ConfigFile.getConfigFileFromDiskOrInMemory` already does the error reporting
if (!configFile)
return;
exports.configFileUpdated.emit(configFile);
exports.projectFilePathsUpdated.emit({ filePaths: configFile.project.files });
// Set the active project (the project we get returned might not be the active project name)
exports.activeProjectConfigDetails = projectConfig;
exports.activeProjectConfigDetailsUpdated.emit(exports.activeProjectConfigDetails);
}
exports.syncCore = syncCore;
var syncDebounced = utils.debounce(sync, 1000);
/**
* Files changing on disk
*/
function fileListingDelta(delta) {
// Check if we have a current project
// If we have a current project does it have some expansion
// If so check if some files need to be *removed* or *added*
if (!configFile)
return;
var projectDir = configFile.projectFileDirectory;
// HEURISTIC : if some delta file path is *under* the `tsconfig.json` path
if (delta.addedFilePaths.some(function (_a) {
var filePath = _a.filePath;
return filePath.startsWith(projectDir);
})
|| delta.removedFilePaths.some(function (_a) {
var filePath = _a.filePath;
return filePath.startsWith(projectDir);
})) {
/**
* Does something match the glob
*/
var matched = delta.addedFilePaths.concat(delta.removedFilePaths).map(function (c) { return c.filePath; }).filter(function (fp) { return utils.isJsOrTs(fp); });
if (matched.length) {
syncDebounced();
}
}
}
exports.fileListingDelta = fileListingDelta;
var ConfigFile;
(function (ConfigFile) {
/**
* This explicilty loads the project from the filesystem to check it for errors
* For Virtual projects it just returns the in memory project
*/
function getConfigFileFromDiskOrInMemory(config) {
if (config.isVirtual) {
return tsconfig.getDefaultInMemoryProject(config.tsconfigFilePath);
}
var filePath = config.tsconfigFilePath;
var _a = tsconfig.getProjectSync(filePath), projectFile = _a.result, error = _a.error;
if (!error) {
clearErrorsInTsconfig(projectFile.projectFilePath);
return projectFile;
}
else {
setErrorsInTsconfig(filePath, [error]);
return undefined;
}
}
ConfigFile.getConfigFileFromDiskOrInMemory = getConfigFileFromDiskOrInMemory;
})(ConfigFile || (ConfigFile = {}));
/**
* As soon as we get a new file listing refresh available projects
*/
flm.filePathsCompleted.on(function (data) {
refreshAvailableProjects();
});
/**
* As soon as edit happens on the project file do a sync
*/
function checkProjectFileChanges(evt) {
var currentConfigFilePath = exports.activeProjectConfigDetails && exports.activeProjectConfigDetails.tsconfigFilePath;
if (evt.filePath == currentConfigFilePath) {
sync();
}
}
var checkProjectFileChangesDebounced = utils.debounce(checkProjectFileChanges, 1000);
fmc.didEdits.on(checkProjectFileChangesDebounced);
fmc.savedFileChangedOnDisk.on(checkProjectFileChangesDebounced);