(diff: ApiDiff)
| 53 | ] as const |
| 54 | |
| 55 | export const renderMarkdownReport = (diff: ApiDiff): string => { |
| 56 | const counts = new Map<string, number>() |
| 57 | for (const change of diff.changes) { |
| 58 | counts.set(change.classification, (counts.get(change.classification) ?? 0) + 1) |
| 59 | } |
| 60 | const authoritative = diff.changes.filter((change) => change.authoritative) |
| 61 | const suggested = diff.changes.filter((change) => !change.authoritative) |
| 62 | const moduleCounts = new Map<string, number>() |
| 63 | for (const change of diff.changes) { |
| 64 | const id = change.headApiId ?? change.baseApiId |
| 65 | const delta = change.delta as { |
| 66 | readonly packageName?: string |
| 67 | readonly from?: string |
| 68 | readonly to?: ReadonlyArray<string> |
| 69 | } | undefined |
| 70 | const module = id?.split("#")[0] ?? delta?.packageName ?? delta?.from ?? delta?.to?.[0] ?? "<package>" |
| 71 | moduleCounts.set(module, (moduleCounts.get(module) ?? 0) + 1) |
| 72 | } |
| 73 | const lines: Array<string> = [ |
| 74 | "# TypeScript API Diff", |
| 75 | "", |
| 76 | `Base: \`${diff.base.ref}\` (\`${diff.base.sha}\`)`, |
| 77 | "", |
| 78 | `Head: \`${diff.head.ref}\` (\`${diff.head.sha}\`)`, |
| 79 | "", |
| 80 | "This report describes structural API changes and review confidence; it is not a semantic-version compatibility claim.", |
| 81 | "", |
| 82 | "## Summary", |
| 83 | "", |
| 84 | "| Classification | Count |", |
| 85 | "| --- | ---: |", |
| 86 | ...[...counts].sort(([left], [right]) => left.localeCompare(right)) |
| 87 | .map(([classification, count]) => `| ${escapeCell(classification)} | ${count} |`), |
| 88 | "", |
| 89 | "## Changes by module", |
| 90 | "", |
| 91 | "| Domain | Module | Count |", |
| 92 | "| --- | --- | ---: |", |
| 93 | ...[...moduleCounts].sort(([left], [right]) => left.localeCompare(right)).map(([module, count]) => { |
| 94 | const unstable = module.match(/^effect\/unstable\/([^/]+)/) |
| 95 | const domain = unstable?.[1] === undefined |
| 96 | ? module.startsWith("@effect/") |
| 97 | ? module.split("/").slice(0, 2).join("/") |
| 98 | : "stable" |
| 99 | : `unstable/${unstable[1]}` |
| 100 | return `| ${escapeCell(domain)} | ${escapeCell(module)} | ${count} |` |
| 101 | }), |
| 102 | "" |
| 103 | ] |
| 104 | const sections = [ |
| 105 | [ |
| 106 | "Stable API changes", |
| 107 | authoritative.filter((change) => |
| 108 | !(change.baseApiId ?? change.headApiId ?? JSON.stringify(change.delta)).includes("/unstable/") |
| 109 | ) |
| 110 | ], |
| 111 | [ |
| 112 | "Unstable API changes", |
no test coverage detected