Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173import { browser } from "$app/environment";import { MAX_INLINE_SIZE } from "./blob";import { blob } from "./gitclient";import { diffFileKind, diffFileStat, diffSideExists, getDiff, getInterdiff, type MirrorFileDiff, type MirrorFileKind} from "./gitmirror";import { pierreDiffOptions, type DiffStyle } from "$lib/components/repo/pierre";import type { FileContents } from "@pierre/diffs";import type { BobbinContext } from "./client";import type { GitTarget } from "./gitclient";import type { DiffStat } from "./diff";import type * as GetDiff from "./lexicons/types/sh/tangled/git/temp2/getDiff";import type * as GetInterdiff from "./lexicons/types/sh/tangled/git/temp2/getInterdiff";
const FILE_LIMIT = 25;
export interface PullDiffFile { // stable across sides, used for anchors and the prerender map key: string; name: string; oldName?: string; kind: MirrorFileKind; stat: { insertions: number; deletions: number }; // set when there is nothing to render: binary, submodule, or over the cap note?: string; oldFile?: FileContents; newFile?: FileContents;}
export interface PullDiff { files: PullDiffFile[]; stat: DiffStat; // how many files were listed but not fetched truncated: number; // server-prerendered shadow dom per file key, only for the initial ssr prerendered?: Record<string, string>;}
// the diff endpoint answers metadata only — `diffSrc` is path/oid/size/flags and// a hunk is aligned line numbers — so the text comes from the blob endpoint and// pierre diffs the two sides itselfconst sideContents = async ( git: GitTarget, ref: string, src: MirrorFileDiff["lhsSrc"]): Promise<string | null> => { // a side that doesn't exist at this ref is an empty side, not a failed fetch if (!diffSideExists(src)) return ""; const output = await blob(git, { ref, path: src.path }, MAX_INLINE_SIZE).catch(() => null); if (!output || output.encoding !== "utf-8") return null; return output.content ?? "";};
// both sides of one file, `null` for a side whose text could not be readtype Sides = (file: MirrorFileDiff) => Promise<[string | null, string | null]>;
const buildDiff = async ( diffs: MirrorFileDiff[], style: DiffStyle, sides: Sides): Promise<PullDiff> => { const stat: DiffStat = { insertions: 0, deletions: 0, files_changed: diffs.length }; const files = await Promise.all( diffs.map(async (file, index): Promise<PullDiffFile> => { const fileStat = diffFileStat(file); stat.insertions += fileStat.insertions; stat.deletions += fileStat.deletions;
const kind = diffFileKind(file); const name = file.rhsSrc.path || file.lhsSrc.path; const base: PullDiffFile = { key: `${file.lhsSrc.path}→${file.rhsSrc.path}`, name, oldName: kind === "renamed" ? file.lhsSrc.path : undefined, kind, stat: fileStat };
if (file.lhsSrc.isSubmodule || file.rhsSrc.isSubmodule) { return { ...base, note: "Submodule pointer, not shown." }; } if (file.lhsSrc.isBinary || file.rhsSrc.isBinary) { return { ...base, note: "This is a binary file and will not be displayed." }; } if (index >= FILE_LIMIT) { return { ...base, note: "Not rendered: too many files in this diff." }; }
const [oldContents, newContents] = await sides(file); if (oldContents === null || newContents === null) { return { ...base, note: "Contents could not be loaded." }; }
return { ...base, // pierre infers the language from the name, same as the blob page oldFile: { name: file.lhsSrc.path || name, contents: oldContents }, newFile: { name, contents: newContents } }; }) );
return { files, stat, truncated: Math.max(diffs.length - FILE_LIMIT, 0), prerendered: await prerender(files, style) };};
export const loadPullDiff = async ( ctx: BobbinContext, git: GitTarget, params: GetDiff.$params, style: DiffStyle): Promise<PullDiff> => { const { diffs } = await getDiff(ctx, params); return buildDiff(diffs, style, (file) => Promise.all([ // the left side is read at baseCommit, while gitmirror diffs from // merge_base(base, head). identical when base is the merge base, which // is what a pull version stores sideContents(git, params.baseCommit, file.lhsSrc), sideContents(git, params.headCommit, file.rhsSrc) ]) );};
// an interdiff's right side is version2's head tree, so it reads like any other// diff — but its left side is a tree gitmirror rebased in memory for this one// request, reachable by no ref, so that text can only arrive inlinedexport const loadPullInterdiff = async ( ctx: BobbinContext, git: GitTarget, params: GetInterdiff.$params, style: DiffStyle): Promise<PullDiff> => { const { diffs } = await getInterdiff(ctx, params); return buildDiff(diffs, style, async (file) => [ diffSideExists(file.lhsSrc) ? (file.lhsSrc.content ?? null) : "", await sideContents(git, params.headCommit2, file.rhsSrc) ]);};
// the first paint ships highlighted, like the commit page doesconst prerender = async ( files: PullDiffFile[], style: DiffStyle): Promise<Record<string, string> | undefined> => { if (browser) return undefined; const { preloadMultiFileDiff } = await import("@pierre/diffs/ssr"); const options = pierreDiffOptions(style); const entries = await Promise.all( files .filter((file) => file.oldFile && file.newFile) .map(async (file) => { const { prerenderedHTML } = await preloadMultiFileDiff({ oldFile: file.oldFile as FileContents, newFile: file.newFile as FileContents, options }); return [file.key, prerenderedHTML] as const; }) ); return Object.fromEntries(entries);};