UNPKG

@jqassistant/ts-lce

Version:

Tool to extract language concepts from a TypeScript codebase and export them to a JSON file.

115 lines (114 loc) 5.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProjectUtils = void 0; const typescript_1 = require("typescript"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const file_utils_1 = require("./file.utils"); /** * Utlity class that provides functionality with regard to TypeScript projects and their configurations */ class ProjectUtils { /** * Normalizes all paths contained within a `LCEProjectInfo` using `FileUtils.normalizePath` */ static normalizeProjectInfo(projectInfo) { return { rootPath: file_utils_1.FileUtils.normalizePath(projectInfo.rootPath), configPath: file_utils_1.FileUtils.normalizePath(projectInfo.configPath), subProjectPaths: projectInfo.subProjectPaths.map(path => file_utils_1.FileUtils.normalizePath(path)), sourceFilePaths: projectInfo.sourceFilePaths.map(path => file_utils_1.FileUtils.normalizePath(path)), tsConfig: projectInfo.tsConfig }; } /** * Scans recursively for projects inside a given directory. * If a project has references (via tsconfig.json) to other projects, they are also scanned (no matter if they are inside or outside the scanRoot). * All information about scanned projects and referenced subprojects are returned in a flat array. */ static async determineProjects(scanRoot) { const result = []; const dirsToScan = [path_1.default.resolve(scanRoot)]; while (dirsToScan.length > 0) { const projectRoot = dirsToScan[0]; const tsConfigPath = path_1.default.join(projectRoot, "tsconfig.json"); if (fs_1.default.existsSync(tsConfigPath)) { result.push(...this.getProjectInfo(projectRoot, 'tsconfig.json')); } else { // add all subdirectories as potential project candidates fs_1.default.readdirSync(projectRoot).forEach(file => { const subdir = path_1.default.join(projectRoot, file); if (fs_1.default.statSync(subdir).isDirectory() && file !== "node_modules") { dirsToScan.push(subdir); } }); } dirsToScan.splice(0, 1); } // filter out duplicates const seen = []; return result.filter(pi => { if (seen.includes(pi.configPath)) { return false; } else { seen.push(pi.configPath); return true; } }); } static getConfigFileName(rawConfigPath) { const pathIsADirectory = fs_1.default.statSync(rawConfigPath).isDirectory(); const defaultConfigFileName = 'tsconfig.json'; return pathIsADirectory ? path_1.default.join(rawConfigPath, defaultConfigFileName) : rawConfigPath; } static getProjectInfo(projectPath, configFileName) { const result = []; const tsConfig = this.parseTsConfig(projectPath, configFileName); const subProjectPaths = []; if (tsConfig.projectReferences) { for (const ref of tsConfig.projectReferences) { const referencedConfigFileName = this.getConfigFileName(ref.path); const subProjectInfos = this.getProjectInfo(path_1.default.dirname(referencedConfigFileName), path_1.default.basename(referencedConfigFileName)); subProjectPaths.push(...subProjectInfos.map(spi => file_utils_1.FileUtils.normalizePath(spi.configPath))); result.push(...subProjectInfos); } } let rootPath = projectPath; // if project root is descendant of configured root directory use the configured one if (path_1.default.resolve(projectPath).startsWith(path_1.default.resolve(projectPath, tsConfig.options.rootDir))) { rootPath = path_1.default.resolve(projectPath, tsConfig.options.rootDir); } result.push({ rootPath: file_utils_1.FileUtils.normalizePath(rootPath), configPath: file_utils_1.FileUtils.normalizePath(path_1.default.join(projectPath, configFileName)), subProjectPaths: subProjectPaths, sourceFilePaths: tsConfig.fileNames.map(fn => file_utils_1.FileUtils.normalizePath(fn)), tsConfig }); return result; } static parseTsConfig(projectRoot, configFile) { const tsConfigPath = path_1.default.join(projectRoot, configFile); const configFileText = fs_1.default.readFileSync(tsConfigPath, 'utf8'); const configFileSourceFile = (0, typescript_1.createSourceFile)(configFile, configFileText, typescript_1.ScriptTarget.JSON); // Parse the tsconfig.json const parsedCommandLine = (0, typescript_1.parseJsonSourceFileConfigFileContent)(configFileSourceFile, typescript_1.sys, path_1.default.dirname(tsConfigPath)); // explicitly set rootDir option to default value, if not set manually if (!parsedCommandLine.options.rootDir) { if (parsedCommandLine.options.composite || parsedCommandLine.fileNames.length === 0) { parsedCommandLine.options.rootDir = projectRoot; } else { parsedCommandLine.options.rootDir = file_utils_1.FileUtils.commonDir(parsedCommandLine.fileNames); } } return parsedCommandLine; } } exports.ProjectUtils = ProjectUtils;