UNPKG

@code-pushup/coverage-plugin

Version:
107 lines 5.45 kB
import path from 'node:path'; import { importModule, logger, pluralize, pluralizeToken, stringifyError, } from '@code-pushup/utils'; import { formatMetaLog } from '../format.js'; /** * Resolves the cached project graph for the current Nx workspace. * First tries to read cache and if not possible, go for the async creation. */ async function resolveCachedProjectGraph() { const { readCachedProjectGraph, createProjectGraphAsync } = await import('@nx/devkit'); try { return readCachedProjectGraph(); } catch (error) { logger.warn(`Could not read cached project graph, falling back to async creation - ${stringifyError(error)}`); return await createProjectGraphAsync({ exitOnError: false }); } } /** * @param targets nx targets to be used for measuring coverage, test by default * @returns An array of coverage result information for the coverage plugin. */ export async function getNxCoveragePaths(targets = ['test']) { const { nodes } = await resolveCachedProjectGraph(); const coverageResultsPerTarget = Object.fromEntries(await Promise.all(targets.map(async (target) => { const relevantNodes = Object.values(nodes).filter(graph => hasNxTarget(graph, target)); return [ target, await Promise.all(relevantNodes.map(({ data }) => getCoveragePathsForTarget(data, target))), ]; }))); const coverageResults = Object.values(coverageResultsPerTarget).flat(); logger.info(formatMetaLog(`Inferred ${pluralizeToken('coverage report', coverageResults.length)} from Nx projects with ${pluralize('target', targets.length)} ${targets.length === 1 ? targets[0] : Object.entries(coverageResultsPerTarget) .map(([target, results]) => `${target} (${results.length})`) .join(' and ')}`)); logger.debug(formatMetaLog(coverageResults .map(result => `• ${typeof result === 'string' ? result : result.resultsPath}`) .join('\n'))); return coverageResults; } function hasNxTarget(project, target) { return project.data.targets != null && target in project.data.targets; } export async function getCoveragePathsForTarget(project, target) { const targetConfig = project.targets?.[target]; if (!targetConfig) { throw new Error(`No configuration found for target ${target} in project ${project.name}`); } if (targetConfig.executor?.includes('@nx/vite')) { return getCoveragePathForVitest(targetConfig.options, project, target); } if (targetConfig.executor?.includes('@nx/jest')) { return getCoveragePathForJest(targetConfig.options, project, target); } throw new Error(`Unsupported executor ${targetConfig.executor}. Only @nx/vite and @nx/jest are currently supported.`); } export async function getCoveragePathForVitest(options, project, target) { const { normalizeViteConfigFilePathWithTree } = await import('@nx/vite'); const config = normalizeViteConfigFilePathWithTree( // HACK: only tree.exists is called, so injecting existSync from node:fs instead // eslint-disable-next-line @typescript-eslint/consistent-type-assertions { exists: (await import('node:fs')).existsSync }, project.root, options.configFile); if (!config) { throw new Error(`Could not find Vitest config file for target ${target} in project ${project.name}`); } const vitestConfig = await importModule({ filepath: config, format: 'esm', }); const reportsDirectory = options.reportsDirectory ?? vitestConfig.test.coverage?.reportsDirectory; const reporter = vitestConfig.test.coverage?.reporter; if (reportsDirectory == null) { throw new Error(`Vitest coverage configuration at ${config} does not include coverage path for target ${target} in project ${project.name}. Add the path under coverage > reportsDirectory.`); } if (!reporter?.some(format => format === 'lcov' || format === 'lcovonly')) { throw new Error(`Vitest coverage configuration at ${config} does not include LCOV report format for target ${target} in project ${project.name}. Add 'lcov' format under coverage > reporter.`); } if (path.isAbsolute(reportsDirectory)) { return path.join(reportsDirectory, 'lcov.info'); } return { pathToProject: project.root, resultsPath: path.join(project.root, reportsDirectory, 'lcov.info'), }; } export async function getCoveragePathForJest(options, project, target) { const { jestConfig } = options; const testConfig = await importModule({ filepath: jestConfig, }); const { coverageDirectory, coverageReporters } = { ...testConfig, ...options, }; if (coverageDirectory == null) { throw new Error(`Jest coverage configuration at ${jestConfig} does not include coverage path for target ${target} in ${project.name}. Add the path under coverageDirectory.`); } if (!coverageReporters?.includes('lcov') && !('preset' in testConfig)) { throw new Error(`Jest coverage configuration at ${jestConfig} does not include LCOV report format for target ${target} in ${project.name}. Add 'lcov' format under coverageReporters.`); } if (path.isAbsolute(coverageDirectory)) { return path.join(coverageDirectory, 'lcov.info'); } return path.join(project.root, coverageDirectory, 'lcov.info'); } //# sourceMappingURL=coverage-paths.js.map