diff --git a/packages/canyon-collect/prisma/schema.prisma b/packages/canyon-collect/prisma/schema.prisma index 58fd94d3..16fa2c83 100755 --- a/packages/canyon-collect/prisma/schema.prisma +++ b/packages/canyon-collect/prisma/schema.prisma @@ -13,6 +13,7 @@ model Coverage { sha String branch String compareTarget String @map("compare_target") + instrumentCwd String @map("instrument_cwd") //是build时的路径,因为要与sourceMap对应 provider String buildProvider String @map("build_provider") // 通过侦测CI环境变量来判断 buildID String @map("build_id") diff --git a/packages/canyon-collect/src/apps/collect/models/coverage.model.ts b/packages/canyon-collect/src/apps/collect/models/coverage.model.ts index e3bb663e..a110dc85 100644 --- a/packages/canyon-collect/src/apps/collect/models/coverage.model.ts +++ b/packages/canyon-collect/src/apps/collect/models/coverage.model.ts @@ -15,6 +15,7 @@ export const coverageObj = { summary: '', hit: '', map: '', + instrumentCwd: '', // createdAt: new Date(), // updatedAt: new Date(), }; diff --git a/packages/canyon-collect/src/apps/collect/services/coverage-client.service.ts b/packages/canyon-collect/src/apps/collect/services/coverage-client.service.ts index e53e0ea3..6dd75d9b 100755 --- a/packages/canyon-collect/src/apps/collect/services/coverage-client.service.ts +++ b/packages/canyon-collect/src/apps/collect/services/coverage-client.service.ts @@ -5,7 +5,6 @@ import { compressedData, decompressedData } from '../../../utils/zstd'; import { formatReportObject, regularData, - remapCoverage, } from '../../../utils/coverage'; import { mergeCoverageMap } from 'canyon-data'; diff --git a/packages/canyon-collect/src/apps/collect/services/coverage-map-client.service.ts b/packages/canyon-collect/src/apps/collect/services/coverage-map-client.service.ts index 19040b08..6bebc4f1 100755 --- a/packages/canyon-collect/src/apps/collect/services/coverage-map-client.service.ts +++ b/packages/canyon-collect/src/apps/collect/services/coverage-map-client.service.ts @@ -65,6 +65,7 @@ export class CoverageMapClientService { summary: Buffer.from([]), hit: Buffer.from([]), map: compressedFormatCoverageStr, + instrumentCwd: instrumentCwd, }, }) .then((r) => { diff --git a/packages/canyon-collect/src/utils/coverage.ts b/packages/canyon-collect/src/utils/coverage.ts index 6790963b..5fb9fc97 100644 --- a/packages/canyon-collect/src/utils/coverage.ts +++ b/packages/canyon-collect/src/utils/coverage.ts @@ -21,7 +21,7 @@ function convertInstrumentCwd({ path, instrumentCwd, projectInstrumentCwd }) { } } // 格式化上报的覆盖率对象 -export async function formatReportObject(c: any) { +export function formatReportObject(c: any) { // 去除斜杠\\ const removeSlash = (x: any) => JSON.parse(JSON.stringify(x).replace(/\\\\/g, '/')); diff --git a/packages/canyon-platform/app/api/cov/map/route.ts b/packages/canyon-platform/app/api/cov/map/route.ts index 501c33a6..14598567 100644 --- a/packages/canyon-platform/app/api/cov/map/route.ts +++ b/packages/canyon-platform/app/api/cov/map/route.ts @@ -1,6 +1,7 @@ // export const dynamic = 'force-static' import prisma from "@/lib/prisma"; import { compressedData, decompressedData } from "@/utils/zstd"; +import { formatReportObject, remapCoverage } from "@/utils/coverage"; // import { decompressedData } from "@/utils/zstd"; // import { compress, decompress } from "@mongodb-js/zstd"; @@ -8,11 +9,13 @@ import { compressedData, decompressedData } from "@/utils/zstd"; export async function GET() { const data = await prisma.coverage.findFirst({ where: { - projectID: "118075", - sha: "91a12fc016b8f0c61dd605a67b38b6c5999fed74", + projectID: "490316875", + sha: "0cacb37b236c9507004505662a0d6bfb018aaec4", }, }); + // console.log(data.instrumentCwd); + const d = await decompressedData(data.map) .then((r) => r.toString()) .then((r) => JSON.parse(r)); @@ -26,8 +29,25 @@ export async function GET() { obj[key] = { ...d[key], ...c[key], + path: key, }; } - - return Response.json(obj); + // console.log(data,'') + function addInstrumentCwd(cov) { + const o = {}; + for (const key in cov) { + o[data.instrumentCwd + "/" + key] = { + ...cov[key], + path: data.instrumentCwd + "/" + key, + }; + } + return o; + } + const r = await remapCoverage(addInstrumentCwd(obj)).then((r) => + formatReportObject({ + coverage: r, + instrumentCwd: data.instrumentCwd, + }), + ); + return Response.json(r.coverage); } diff --git a/packages/canyon-platform/package.json b/packages/canyon-platform/package.json index 3f482b7d..0f7afbfe 100755 --- a/packages/canyon-platform/package.json +++ b/packages/canyon-platform/package.json @@ -34,6 +34,8 @@ "eslint-config-next": "15.0.3", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-source-maps": "^5.0.6", "postcss": "^8", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", diff --git a/packages/canyon-platform/prisma/schema.prisma b/packages/canyon-platform/prisma/schema.prisma index bce31e5d..54b0221a 100644 --- a/packages/canyon-platform/prisma/schema.prisma +++ b/packages/canyon-platform/prisma/schema.prisma @@ -69,6 +69,7 @@ model Coverage { hit Bytes // 特殊逻辑,必须要有map数据,才能上报hit,并且存在cov_type上。 map Bytes + instrumentCwd String @map("instrument_cwd") //是build时的路径,因为要与sourceMap对应 // 通用 createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3) updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(3) diff --git a/packages/canyon-platform/utils/coverage.ts b/packages/canyon-platform/utils/coverage.ts new file mode 100644 index 00000000..e7662f32 --- /dev/null +++ b/packages/canyon-platform/utils/coverage.ts @@ -0,0 +1,74 @@ +import libCoverage from "istanbul-lib-coverage"; +import libSourceMaps from "istanbul-lib-source-maps"; + +// 覆盖率回溯,在覆盖率存储之前转换 +export async function remapCoverage(obj: any) { + const res = await libSourceMaps + .createSourceMapStore() + .transformCoverage(libCoverage.createCoverageMap(obj)); + const { data: data_1 } = res; + const obj_1: any = {}; + for (const dataKey in data_1) { + const x = data_1[dataKey]["data"]; + obj_1[x.path] = x; + } + return obj_1; +} + +function parseInstrumentCwd(instrumentCwd) { + if (instrumentCwd.includes("=>")) { + const instrumentCwdSplit = instrumentCwd.split("=>"); + return [instrumentCwdSplit[0], instrumentCwdSplit[1]]; + } else { + return [instrumentCwd, ""]; + } +} +function convertInstrumentCwd({ path, instrumentCwd, projectInstrumentCwd }) { + if (!projectInstrumentCwd) { + return path.replace(instrumentCwd, ""); + } else { + // 这里需要解析一下instrumentCwd,如果包含"=>",则需要替换。 + const [leftInstrumentCwd, rightInstrumentCwd] = + parseInstrumentCwd(projectInstrumentCwd); + return path + .replace(instrumentCwd, "") + .replace(leftInstrumentCwd, rightInstrumentCwd); + } +} +// 格式化上报的覆盖率对象 +export function formatReportObject(c: any) { + // 去除斜杠\\ + const removeSlash = (x: any) => + JSON.parse(JSON.stringify(x).replace(/\\\\/g, "/")); + // 暂时解决方案,需要解决sourceMap问题 + const coverage = removeSlash(c.coverage); + const instrumentCwd = removeSlash(c.instrumentCwd); + const projectInstrumentCwd = removeSlash(c.projectInstrumentCwd || ""); + const reversePath = (p: string) => { + const a = convertInstrumentCwd({ + path: p, + instrumentCwd, + projectInstrumentCwd, + }); + let b = ""; + // 从第二个字符开始 + for (let i = 1; i < a.length; i++) { + b += a[i]; + } + return "" + b; + }; + const obj: any = {}; + for (const coverageKey in coverage) { + obj[reversePath(coverageKey)] = { + ...coverage[coverageKey], + path: reversePath(coverageKey), + }; + } + + // 确保修改成istanbul格式,去掉start、end为空的情况 + + return { + coverage: obj, + instrumentCwd, + }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1e5a6e29..65918cc2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -208,6 +208,12 @@ importers: eslint-plugin-prettier: specifier: ^5.2.1 version: 5.2.1(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.3.3) + istanbul-lib-coverage: + specifier: ^3.2.2 + version: 3.2.2 + istanbul-lib-source-maps: + specifier: ^5.0.6 + version: 5.0.6 postcss: specifier: ^8 version: 8.4.49