UNPKG

alm

Version:

The best IDE for TypeScript

220 lines (219 loc) 9.02 kB
"use strict"; /** * 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);