import fs from 'fs-extra'; import kleur from 'kleur'; import { type Program } from 'typescript'; import { Plugin } from '../../../plugins/Plugin.js'; import { parseGlobs } from '../../../utils/globs.js'; import { clearTerminal, log, LogLevel, logWithTime, } from '../../../utils/log.js'; import { resolveConfigPaths, resolveCorePkgName, resolvePath, } from '../../../utils/resolve.js'; import { isArray, isUndefined } from '../../../utils/unit.js'; import { analyze } from './analyze.js'; import { compileOnce, transpileModuleOnce } from './compile.js'; import { AnalyzeCommandConfig } from './config.js'; import { discoverAndBuild } from './discover.js'; import { link } from './link.js'; async function normalizeConfig(config: AnalyzeCommandConfig) { const cwd = isUndefined(config.cwd) ? process.cwd() : config.cwd; const normalizedConfig = await resolveConfigPaths(cwd, config); normalizedConfig.pkgName = (await resolveCorePkgName(normalizedConfig.cwd))!; return normalizedConfig; } export async function runAnalyzeCommand( analyzeConfig: AnalyzeCommandConfig, ): Promise { clearTerminal(); const startAnalyzeTime = process.hrtime(); const config = await normalizeConfig(analyzeConfig); const glob: string[] = config.glob ?? []; log(config, LogLevel.Verbose); const startCompileTime = process.hrtime(); const filePaths = await parseGlobs(glob); const program = compileOnce(filePaths); logWithTime(`[${kleur.yellow('@celement')}] \`compile\``, startCompileTime); if (!(await fs.pathExists(config.configFile))) { log( `No configuration file could be found at ${kleur.red(config.configFile)}`, LogLevel.Error, ); return; } const plugins = (await transpileModuleOnce(config.configFile)) as Plugin[]; if (!isArray(plugins)) { log( `Configuration file must default export an array of plugins, found ${kleur.red( typeof plugins, )}`, LogLevel.Error, ); return; } await initPlugins(program, plugins); const noOfFiles = await runPlugins(program, plugins, glob, filePaths); await destroyPlugins(plugins); const noOfFilesText = kleur.green( `${noOfFiles} ${noOfFiles === 1 ? 'file' : 'files'}`, ); logWithTime(`Finished analyzing ${noOfFilesText}`, startAnalyzeTime); } export async function initPlugins( program: Program, plugins: Plugin[], ): Promise { for (let p = 0; p < plugins.length; p += 1) { const plugin = plugins[p]; if (isUndefined(plugin.init)) continue; const startTime = process.hrtime(); await plugin.init(program); logWithTime(`[${kleur.yellow(plugin.name)}] \`init\``, startTime); } } export async function runPlugins( program: Program, plugins: Plugin[], glob: string[], paths?: string[], ): Promise { const validFilePaths = new Set(paths ?? (await parseGlobs(glob))); const sourceFiles = program .getSourceFiles() .filter(sf => validFilePaths.has(resolvePath(sf.fileName))) .sort((sfA, sfB) => (sfA.fileName > sfB.fileName ? 1 : -1)); const checker = program.getTypeChecker(); const components = await discoverAndBuild(checker, plugins, sourceFiles); const linkedComponents = await link(plugins, components, sourceFiles); await analyze(plugins, linkedComponents); return sourceFiles.length; } export async function destroyPlugins(plugins: Plugin[]): Promise { for (let p = 0; p < plugins.length; p += 1) { const plugin = plugins[p]; if (isUndefined(plugin.destroy)) continue; const startTime = process.hrtime(); await plugin.destroy(); logWithTime(`[${kleur.yellow(plugin.name)}] \`destroy\``, startTime); } }