From 91f61b3c36dbb966ad381b8726b108b99546d621 Mon Sep 17 00:00:00 2001 From: Misode Date: Fri, 4 Oct 2024 19:02:53 +0200 Subject: [PATCH] Fix #519 by manually creating the patch when github doesn't give it --- package-lock.json | 27 ++++++++++ package.json | 2 + src/app/Utils.ts | 9 +++- src/app/components/versions/VersionDiff.tsx | 57 +++++++++++++-------- 4 files changed, 71 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index b2927210..7f9ae6e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "deepslate-1.18": "npm:deepslate@0.9.0-beta.9", "deepslate-1.18.2": "npm:deepslate@0.9.0", "deepslate-1.20.4": "npm:deepslate@0.20.1", + "diff": "^7.0.0", "highlight.js": "^11.5.1", "howler": "^2.2.3", "js-yaml": "^3.14.1", @@ -45,6 +46,7 @@ "devDependencies": { "@preact/preset-vite": "^2.4.0", "@rollup/plugin-html": "^1.0.1", + "@types/diff": "^5.2.2", "@types/google.analytics": "0.0.40", "@types/gtag.js": "^0.0.10", "@types/howler": "^2.2.4", @@ -872,6 +874,12 @@ "node": ">= 8.0.0" } }, + "node_modules/@types/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-qVqLpd49rmJA2nZzLVsmfS/aiiBpfVE95dHhPVwG0NmSBAt+riPxnj53wq2oBq5m4Q2RF1IWFEUpnZTgrQZfEQ==", + "dev": true + }, "node_modules/@types/google.analytics": { "version": "0.0.40", "resolved": "https://registry.npmjs.org/@types/google.analytics/-/google.analytics-0.0.40.tgz", @@ -1687,6 +1695,14 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "dev": true }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4972,6 +4988,12 @@ "picomatch": "^2.2.2" } }, + "@types/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-qVqLpd49rmJA2nZzLVsmfS/aiiBpfVE95dHhPVwG0NmSBAt+riPxnj53wq2oBq5m4Q2RF1IWFEUpnZTgrQZfEQ==", + "dev": true + }, "@types/google.analytics": { "version": "0.0.40", "resolved": "https://registry.npmjs.org/@types/google.analytics/-/google.analytics-0.0.40.tgz", @@ -5516,6 +5538,11 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "dev": true }, + "diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==" + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", diff --git a/package.json b/package.json index 2225be45..98ed0ad8 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "deepslate-1.18.2": "npm:deepslate@0.9.0", "deepslate-1.20.4": "npm:deepslate@0.20.1", "deepslate": "^0.22.3", + "diff": "^7.0.0", "highlight.js": "^11.5.1", "howler": "^2.2.3", "js-yaml": "^3.14.1", @@ -51,6 +52,7 @@ "devDependencies": { "@preact/preset-vite": "^2.4.0", "@rollup/plugin-html": "^1.0.1", + "@types/diff": "^5.2.2", "@types/google.analytics": "0.0.40", "@types/gtag.js": "^0.0.10", "@types/howler": "^2.2.4", diff --git a/src/app/Utils.ts b/src/app/Utils.ts index 90877f2b..578d6b33 100644 --- a/src/app/Utils.ts +++ b/src/app/Utils.ts @@ -562,6 +562,11 @@ export function parseGitPatch(patch: string) { let after = 1 for (let i = 0; i < source.length; i += 1) { const line = source[i] + if (line.startsWith('Index: ') || line.startsWith('===') + || line.startsWith('---') || line.startsWith('+++') + || line.startsWith('\\') || line.length === 0) { + continue + } if (line.startsWith('@')) { const match = line.match(/^@@ -(\d+)(?:,(?:\d+))? \+(\d+)(?:,(?:\d+))? @@/) if (!match) throw new Error(`Invalid patch pattern at line ${i+1}: ${line}`) @@ -578,8 +583,8 @@ export function parseGitPatch(patch: string) { } else if (line.startsWith('-')) { result.push({ line, before }) before += 1 - } else if (!line.startsWith('\\')) { - throw new Error(`Invalid patch, got ${line.charAt(0)} at line ${i+1}`) + } else { + throw new Error(`Invalid patch, got '${line.charAt(0)}' at line ${i+1}`) } } return result diff --git a/src/app/components/versions/VersionDiff.tsx b/src/app/components/versions/VersionDiff.tsx index ca18ea90..c567f0eb 100644 --- a/src/app/components/versions/VersionDiff.tsx +++ b/src/app/components/versions/VersionDiff.tsx @@ -1,13 +1,16 @@ -import { useCallback, useEffect, useMemo, useRef } from "preact/hooks" -import { parseGitPatch } from "../../Utils.js" -import { useLocale } from "../../contexts/Locale.jsx" -import { useAsync } from "../../hooks/useAsync.js" -import { useLocalStorage } from "../../hooks/useLocalStorage.js" -import { useSearchParam } from "../../hooks/useSearchParam.js" -import { GitHubCommitFile, fetchVersionDiff } from "../../services/DataFetcher.js" -import { ErrorPanel } from "../ErrorPanel.jsx" -import { Octicon } from "../Octicon.jsx" -import { TreeView, TreeViewGroupRenderer, TreeViewLeafRenderer } from "../TreeView.jsx" +import { createPatch } from 'diff' +import { useCallback, useEffect, useRef } from 'preact/hooks' +import { useLocale } from '../../contexts/Locale.jsx' +import { useAsync } from '../../hooks/useAsync.js' +import { useLocalStorage } from '../../hooks/useLocalStorage.js' +import { useSearchParam } from '../../hooks/useSearchParam.js' +import type { GitHubCommitFile } from '../../services/DataFetcher.js' +import { fetchVersionDiff } from '../../services/DataFetcher.js' +import { parseGitPatch } from '../../Utils.js' +import { ErrorPanel } from '../ErrorPanel.jsx' +import { Octicon } from '../Octicon.jsx' +import type { TreeViewGroupRenderer, TreeViewLeafRenderer } from '../TreeView.jsx' +import { TreeView } from '../TreeView.jsx' const mcmetaRawUrl = 'https://raw.githubusercontent.com/misode/mcmeta' const mcmetaBlobUrl = 'https://github.com/misode/mcmeta/blob' @@ -30,35 +33,45 @@ export function VersionDiff({ version }: Props) { } }, [diffView, setFilename]) - const { file, diff } = useMemo(() => { + const { value, loading } = useAsync(async () => { if (filename === undefined) return { file: undefined, diff: undefined } const file = commit?.files.find(f => f.filename === filename) if (file === undefined) return { file, diff: undefined } - if (file.patch === undefined) { - const match = filename.match(/\.(png|ogg)$/) - if (match) { + let patch = file.patch + if (patch === undefined) { + const isMedia = filename.match(/\.(png|ogg)$/) + const isText = filename.match(/\.(txt|json|mcmeta)$/) + if (isMedia) { return { file, diff: { - type: match[1], + type: isMedia[1], before: file.status === 'added' ? undefined : `${mcmetaRawUrl}/${commit?.parents[0].sha}/${filename}`, after: file.status === 'removed' ? undefined : `${mcmetaRawUrl}/${version}-diff/${filename}`, }, } } else if (file.status === 'renamed') { return { file, diff: [] } - } else { - return { file, diff: new Error('Cannot display diff for this file') } + } else if (isText) { + const [beforeStr, afterStr] = await Promise.all([ + fetch(`${mcmetaRawUrl}/${commit?.parents[0].sha}/${filename}`).then(r => r.text()), + fetch(`${mcmetaRawUrl}/${version}-diff/${filename}`).then(r => r.text()), + ]) + patch = createPatch(filename, beforeStr, afterStr) } } + if (patch === undefined) { + return { file, diff: new Error('Cannot display diff for this file') } + } try { - return { file, diff: parseGitPatch(file.patch) } + return { file, diff: parseGitPatch(patch) } } catch (e) { const error = e as Error error.message = `Failed to show diff: ${error.message}` return { file, diff: error } } }, [filename, commit]) + const { file, diff } = value ?? {} const DiffFolder: TreeViewGroupRenderer = useCallback(({ name, open, onClick }) => { return
@@ -71,7 +84,7 @@ export function VersionDiff({ version }: Props) { return
svg]:shrink-0 select-none ${entry.filename === filename ? 'active' : ''}`} onClick={() => selectFile(entry.filename)} title={entry.filename}> {entry.filename.split('/').at(-1)} {Octicon[`diff_${entry.status}`]} -
+
}, [filename]) useEffect(() => { @@ -106,12 +119,12 @@ export function VersionDiff({ version }: Props) {
file.filename.split('/')} />
- {filename &&
+ {filename &&
- {diff === undefined ? ( + {(diff === undefined || loading) ? ( {locale('loading')} ) : diff instanceof Error ? ( @@ -134,7 +147,7 @@ export function VersionDiff({ version }: Props) { )}
) : <> - {file.previous_filename !== undefined &&
+ {file?.previous_filename !== undefined &&
{file.previous_filename} {filename}
}