Files
upload-gltf/lib/diff-files.ts
T
2026-04-27 11:07:16 +02:00

84 lines
3.0 KiB
TypeScript

// ---------------------------------------------------------------------------
// File diff classification — compares local files against a remote file map
// ---------------------------------------------------------------------------
import { MODEL_EXTENSIONS } from './constants'
import type { FileChange } from './types'
interface PushFile {
path: string
contentBase64: string
}
export interface DiffResult {
/** Map of lowercase filename → change status (for commit message) */
fileChanges: Map<string, FileChange>
/** Files that actually need to be pushed (new or changed) */
changedFilesToPush: PushFile[]
/** Filenames that were on remote but not in the new upload */
deletedFileNames: string[]
/** Full paths for deletion on remote */
deletePaths: string[]
}
/**
* Classify each file as new, changed, or unchanged by comparing against
* the remote file map.
*
* Rules:
* - Models: always re-pushed,
* but marked as 'unchanged' in the commit message when the folder already
* exists (we keep the current behavior of always delivering the model file).
* - Textures: compared by size (not compressed, reliable).
* - Orphan remote files: classified as deletions.
*/
export function classifyFileChanges(
filesToPush: PushFile[],
remoteFileMap: Map<string, number>,
folderPath: string,
): DiffResult {
const fileChanges = new Map<string, FileChange>()
const changedFilesToPush: PushFile[] = []
for (const f of filesToPush) {
const filename = f.path.split('/').pop() ?? ''
const ext = filename.slice(filename.lastIndexOf('.')).toLowerCase()
const isModel = MODEL_EXTENSIONS.has(ext)
if (isModel) {
// Model: always re-push since compression makes size comparison unreliable.
// Mark as 'unchanged' for the commit message when the folder already exists.
const remoteSize = remoteFileMap.get(filename.toLowerCase())
fileChanges.set(filename.toLowerCase(), remoteSize === undefined ? 'new' : 'unchanged')
changedFilesToPush.push(f)
} else {
// Texture: compare by size
const localSize = Buffer.from(f.contentBase64, 'base64').length
const remoteSize = remoteFileMap.get(filename.toLowerCase())
if (remoteSize === undefined) {
fileChanges.set(filename.toLowerCase(), 'new')
changedFilesToPush.push(f)
} else if (remoteSize !== localSize) {
fileChanges.set(filename.toLowerCase(), 'changed')
changedFilesToPush.push(f)
} else {
fileChanges.set(filename.toLowerCase(), 'unchanged')
}
}
}
// Files on remote not in the new upload → deleted (orphans)
const newFileNames = new Set(filesToPush.map((f) => (f.path.split('/').pop() ?? '').toLowerCase()))
const deletedFileNames: string[] = []
const deletePaths: string[] = []
for (const [name] of remoteFileMap) {
if (!newFileNames.has(name)) {
deletedFileNames.push(name)
deletePaths.push(`${folderPath}/${name}`)
}
}
return { fileChanges, changedFilesToPush, deletedFileNames, deletePaths }
}