shell-dsl 0.0.34 → 0.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -5
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/fs/memfs-adapter.cjs +56 -2
- package/dist/cjs/src/fs/memfs-adapter.cjs.map +3 -3
- package/dist/cjs/src/fs/real-fs.cjs +134 -3
- package/dist/cjs/src/fs/real-fs.cjs.map +3 -3
- package/dist/cjs/src/fs/special-files.cjs +3 -2
- package/dist/cjs/src/fs/special-files.cjs.map +3 -3
- package/dist/cjs/src/fs/web-fs.cjs +72 -3
- package/dist/cjs/src/fs/web-fs.cjs.map +3 -3
- package/dist/cjs/src/index.cjs +2 -7
- package/dist/cjs/src/index.cjs.map +3 -3
- package/dist/cjs/src/interpreter/interpreter.cjs +186 -68
- package/dist/cjs/src/interpreter/interpreter.cjs.map +3 -3
- package/dist/cjs/src/parser/ast.cjs +5 -25
- package/dist/cjs/src/parser/ast.cjs.map +3 -3
- package/dist/cjs/src/parser/index.cjs +2 -7
- package/dist/cjs/src/parser/index.cjs.map +3 -3
- package/dist/cjs/src/parser/parser.cjs +132 -82
- package/dist/cjs/src/parser/parser.cjs.map +3 -3
- package/dist/cjs/src/types.cjs.map +2 -2
- package/dist/cjs/src/vcs/content.cjs +106 -0
- package/dist/cjs/src/vcs/content.cjs.map +10 -0
- package/dist/cjs/src/vcs/diff.cjs +72 -28
- package/dist/cjs/src/vcs/diff.cjs.map +3 -3
- package/dist/cjs/src/vcs/index.cjs.map +1 -1
- package/dist/cjs/src/vcs/objects.cjs +141 -0
- package/dist/cjs/src/vcs/objects.cjs.map +10 -0
- package/dist/cjs/src/vcs/rules.cjs +6 -3
- package/dist/cjs/src/vcs/rules.cjs.map +3 -3
- package/dist/cjs/src/vcs/snapshot.cjs +89 -39
- package/dist/cjs/src/vcs/snapshot.cjs.map +3 -3
- package/dist/cjs/src/vcs/storage.cjs +44 -3
- package/dist/cjs/src/vcs/storage.cjs.map +3 -3
- package/dist/cjs/src/vcs/text-diff.cjs +219 -0
- package/dist/cjs/src/vcs/text-diff.cjs.map +10 -0
- package/dist/cjs/src/vcs/vcs.cjs +108 -61
- package/dist/cjs/src/vcs/vcs.cjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/src/fs/memfs-adapter.mjs +57 -2
- package/dist/mjs/src/fs/memfs-adapter.mjs.map +3 -3
- package/dist/mjs/src/fs/real-fs.mjs +135 -3
- package/dist/mjs/src/fs/real-fs.mjs.map +3 -3
- package/dist/mjs/src/fs/special-files.mjs +3 -2
- package/dist/mjs/src/fs/special-files.mjs.map +3 -3
- package/dist/mjs/src/fs/web-fs.mjs +72 -3
- package/dist/mjs/src/fs/web-fs.mjs.map +3 -3
- package/dist/mjs/src/index.mjs +4 -14
- package/dist/mjs/src/index.mjs.map +3 -3
- package/dist/mjs/src/interpreter/interpreter.mjs +186 -68
- package/dist/mjs/src/interpreter/interpreter.mjs.map +3 -3
- package/dist/mjs/src/parser/ast.mjs +5 -25
- package/dist/mjs/src/parser/ast.mjs.map +3 -3
- package/dist/mjs/src/parser/index.mjs +4 -14
- package/dist/mjs/src/parser/index.mjs.map +3 -3
- package/dist/mjs/src/parser/parser.mjs +132 -82
- package/dist/mjs/src/parser/parser.mjs.map +3 -3
- package/dist/mjs/src/types.mjs.map +2 -2
- package/dist/mjs/src/vcs/content.mjs +66 -0
- package/dist/mjs/src/vcs/content.mjs.map +10 -0
- package/dist/mjs/src/vcs/diff.mjs +72 -28
- package/dist/mjs/src/vcs/diff.mjs.map +3 -3
- package/dist/mjs/src/vcs/index.mjs.map +1 -1
- package/dist/mjs/src/vcs/objects.mjs +106 -0
- package/dist/mjs/src/vcs/objects.mjs.map +10 -0
- package/dist/mjs/src/vcs/rules.mjs +6 -3
- package/dist/mjs/src/vcs/rules.mjs.map +3 -3
- package/dist/mjs/src/vcs/snapshot.mjs +89 -39
- package/dist/mjs/src/vcs/snapshot.mjs.map +3 -3
- package/dist/mjs/src/vcs/storage.mjs +45 -3
- package/dist/mjs/src/vcs/storage.mjs.map +3 -3
- package/dist/mjs/src/vcs/text-diff.mjs +179 -0
- package/dist/mjs/src/vcs/text-diff.mjs.map +10 -0
- package/dist/mjs/src/vcs/vcs.mjs +115 -63
- package/dist/mjs/src/vcs/vcs.mjs.map +3 -3
- package/dist/types/src/fs/real-fs.d.ts +12 -1
- package/dist/types/src/index.d.ts +4 -4
- package/dist/types/src/interpreter/interpreter.d.ts +15 -1
- package/dist/types/src/parser/ast.d.ts +34 -38
- package/dist/types/src/parser/index.d.ts +2 -2
- package/dist/types/src/parser/parser.d.ts +4 -1
- package/dist/types/src/types.d.ts +10 -0
- package/dist/types/src/vcs/content.d.ts +6 -0
- package/dist/types/src/vcs/diff.d.ts +10 -7
- package/dist/types/src/vcs/index.d.ts +1 -1
- package/dist/types/src/vcs/objects.d.ts +22 -0
- package/dist/types/src/vcs/snapshot.d.ts +13 -8
- package/dist/types/src/vcs/storage.d.ts +7 -1
- package/dist/types/src/vcs/text-diff.d.ts +1 -0
- package/dist/types/src/vcs/types.d.ts +20 -5
- package/dist/types/src/vcs/vcs.d.ts +6 -0
- package/package.json +7 -2
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/vcs/snapshot.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { VirtualFS } from \"../types.cjs\";\nimport type { TreeManifest, FileEntry, TreeEntry } from \"./types.cjs\";\nimport { matchVCSPath, VCSRules } from \"./rules.cjs\";\nimport { walkTreeEntries } from \"./walk.cjs\";\n\n/**\n * Build a TreeManifest from the current working tree.\n */\nexport async function buildTreeManifest(\n fs: VirtualFS,\n rootPath: string,\n options?: {\n rules?: VCSRules;\n trackedPaths?: Iterable<string>;\n },\n): Promise<TreeManifest> {\n const manifest: TreeManifest = {};\n const rules = options?.rules ?? new VCSRules({ internalDirName: \".vcs\" });\n const trackedPaths = new Set(options?.trackedPaths ?? []);\n const entries = await walkTreeEntries(fs, rootPath, {\n enterDirectory: (relPath) => rules.shouldEnterDirectory(relPath, trackedPaths),\n includeFile: (relPath) => rules.shouldIncludeWorkingFile(relPath, trackedPaths),\n includeDirectory: (relPath, info) =>\n info.empty && rules.shouldIncludeEmptyDirectory(relPath, trackedPaths),\n });\n\n for (const entry of entries) {\n if (entry.kind === \"directory\") {\n manifest[entry.path] = { kind: \"directory\", size: 0 };\n continue;\n }\n\n const fullPath = fs.resolve(rootPath, entry.path);\n const content = await fs.readFile(fullPath);\n const buf = Buffer.from(content);\n manifest[entry.path] = {\n kind: \"file\",\n content: buf.toString(\"base64\"),\n size: buf.length,\n };\n }\n\n return manifest;\n}\n\n/**\n * Build a TreeManifest for only the specified relative paths.\n */\nexport async function buildPartialManifest(\n fs: VirtualFS,\n rootPath: string,\n paths: string[],\n): Promise<TreeManifest> {\n const manifest: TreeManifest = {};\n\n for (const relPath of paths) {\n const fullPath = fs.resolve(rootPath, relPath);\n if (!(await fs.exists(fullPath))) continue;\n const stat = await fs.stat(fullPath);\n if (stat.isDirectory()) {\n const entries = await fs.readdir(fullPath);\n if (entries.length === 0) {\n manifest[relPath] = { kind: \"directory\", size: 0 };\n }\n continue;\n }\n if (!stat.isFile()) continue;\n\n const content = await fs.readFile(fullPath);\n const buf = Buffer.from(content);\n manifest[relPath] = {\n kind: \"file\",\n content: buf.toString(\"base64\"),\n size: buf.length,\n };\n }\n\n return manifest;\n}\n\n/**\n * Restore a working tree from a TreeManifest.\n *\n * If `fullRestore` is true, deletes working tree files not in the manifest.\n * If `paths` is provided, only restores matching files.\n */\nexport async function restoreTree(\n fs: VirtualFS,\n rootPath: string,\n manifest: TreeManifest,\n options?: {\n fullRestore?: boolean;\n paths?: string[];\n rules?: VCSRules;\n trackedPaths?: Iterable<string>;\n },\n): Promise<void> {\n const fullRestore = options?.fullRestore ?? false;\n const rules = options?.rules ?? new VCSRules({ internalDirName: \".vcs\" });\n const trackedPaths = new Set(options?.trackedPaths ?? []);\n const scopePatterns = options?.paths ?? null;\n const scopedEntries = Object.entries(manifest)\n .filter(([relPath]) => isPathInScope(relPath, scopePatterns))\n .sort(([a], [b]) => a.localeCompare(b));\n const targetPaths = new Set(scopedEntries.map(([relPath]) => relPath));\n const requiredDirectories = collectRequiredDirectories(scopedEntries);\n const shouldDeleteExtras = fullRestore || scopePatterns !== null;\n\n if (shouldDeleteExtras) {\n const currentEntries = await walkTreeEntries(fs, rootPath, {\n enterDirectory: (relPath) => !rules.isInternalPath(relPath),\n includeFile: (relPath) => rules.shouldIncludeRestoreScanFile(relPath),\n includeDirectory: () => true,\n });\n\n for (const current of currentEntries) {\n if (current.kind !== \"file\") continue;\n if (!isPathInScope(current.path, scopePatterns)) continue;\n if (targetPaths.has(current.path)) continue;\n if (rules.shouldPreserveUntrackedIgnored(current.path, trackedPaths)) continue;\n await fs.rm(fs.resolve(rootPath, current.path));\n }\n }\n\n for (const directory of [...requiredDirectories].sort(comparePathDepth)) {\n await ensureDirectoryExists(fs, fs.resolve(rootPath, directory));\n }\n\n for (const [relPath, entry] of scopedEntries) {\n if (isDirectoryEntry(entry)) {\n await ensureDirectoryExists(fs, fs.resolve(rootPath, relPath));\n continue;\n }\n await writeFileFromEntry(fs, rootPath, relPath, entry);\n }\n\n if (shouldDeleteExtras) {\n const currentEntries = await walkTreeEntries(fs, rootPath, {\n enterDirectory: (relPath) => !rules.isInternalPath(relPath),\n includeFile: () => true,\n includeDirectory: () => true,\n });\n\n const directories = currentEntries\n .filter((entry) => entry.kind === \"directory\")\n .map((entry) => entry.path)\n .filter((relPath) => isPathInScope(relPath, scopePatterns))\n .sort((a, b) => comparePathDepth(b, a));\n\n for (const relPath of directories) {\n if (requiredDirectories.has(relPath)) continue;\n if (rules.shouldPreserveUntrackedIgnored(relPath, trackedPaths)) continue;\n const fullPath = fs.resolve(rootPath, relPath);\n if (await isEmptyDirectory(fs, fullPath)) {\n await fs.rm(fullPath);\n }\n }\n }\n}\n\nasync function writeFileFromEntry(\n fs: VirtualFS,\n rootPath: string,\n relPath: string,\n entry: FileEntry,\n): Promise<void> {\n const fullPath = fs.resolve(rootPath, relPath);\n await ensureDirectoryExists(fs, fs.dirname(fullPath));\n await removeDirectoryAtPath(fs, fullPath);\n\n const buf = Buffer.from(entry.content, \"base64\");\n await fs.writeFile(fullPath, buf);\n}\n\nfunction isDirectoryEntry(entry: TreeEntry): entry is Extract<TreeEntry, { kind: \"directory\" }> {\n return entry.kind === \"directory\";\n}\n\nfunction isPathInScope(relPath: string, patterns: string[] | null): boolean {\n if (!patterns || patterns.length === 0) return true;\n return patterns.some((pattern) => matchVCSPath(pattern, relPath));\n}\n\nfunction collectRequiredDirectories(entries: Array<[string, TreeEntry]>): Set<string> {\n const directories = new Set<string>();\n\n for (const [relPath, entry] of entries) {\n if (isDirectoryEntry(entry)) {\n directories.add(relPath);\n }\n for (const parent of parentDirectories(relPath)) {\n directories.add(parent);\n }\n }\n\n return directories;\n}\n\nfunction parentDirectories(relPath: string): string[] {\n const parts = relPath.split(\"/\").filter(Boolean);\n const parents: string[] = [];\n\n for (let i = 1; i < parts.length; i++) {\n parents.push(parts.slice(0, i).join(\"/\"));\n }\n\n return parents;\n}\n\nfunction comparePathDepth(a: string, b: string): number {\n const depthA = a.split(\"/\").filter(Boolean).length;\n const depthB = b.split(\"/\").filter(Boolean).length;\n if (depthA !== depthB) return depthA - depthB;\n return a.localeCompare(b);\n}\n\nasync function ensureDirectoryExists(fs: VirtualFS, dirPath: string): Promise<void> {\n const parent = fs.dirname(dirPath);\n if (parent !== dirPath) {\n await ensureDirectoryExists(fs, parent);\n }\n\n if (await fs.exists(dirPath)) {\n const stat = await fs.stat(dirPath);\n if (stat.isDirectory()) return;\n await fs.rm(dirPath, { recursive: true, force: true });\n }\n\n await fs.mkdir(dirPath, { recursive: true });\n}\n\nasync function removeDirectoryAtPath(fs: VirtualFS, path: string): Promise<void> {\n if (!(await fs.exists(path))) return;\n const stat = await fs.stat(path);\n if (stat.isDirectory()) {\n await fs.rm(path, { recursive: true, force: true });\n }\n}\n\nasync function isEmptyDirectory(fs: VirtualFS, dirPath: string): Promise<boolean> {\n try {\n const entries = await fs.readdir(dirPath);\n return entries.length === 0;\n } catch {\n return false;\n }\n}\n"
|
|
5
|
+
"import type { VirtualFS } from \"../types.cjs\";\nimport type { TreeManifest, FileEntry, TreeEntry, VCSIndexEntry } from \"./types.cjs\";\nimport { hashSample, readStreamSample } from \"./content.cjs\";\nimport { matchVCSPath, VCSRules } from \"./rules.cjs\";\nimport { walkTreeEntries } from \"./walk.cjs\";\nimport { VCSObjectStore } from \"./objects.cjs\";\n\nexport interface BuildTreeManifestResult {\n manifest: TreeManifest;\n indexEntries: Record<string, VCSIndexEntry>;\n}\n\n/**\n * Build a TreeManifest from the current working tree.\n */\nexport async function buildTreeManifest(\n fs: VirtualFS,\n rootPath: string,\n options: {\n objectStore: VCSObjectStore;\n rules?: VCSRules;\n trackedPaths?: Iterable<string>;\n indexEntries?: Record<string, VCSIndexEntry>;\n },\n): Promise<BuildTreeManifestResult> {\n const manifest: TreeManifest = {};\n const nextIndexEntries: Record<string, VCSIndexEntry> = {};\n const rules = options.rules ?? new VCSRules({ internalDirName: \".vcs\" });\n const trackedPaths = new Set(options.trackedPaths ?? []);\n const entries = await walkTreeEntries(fs, rootPath, {\n enterDirectory: (relPath) => rules.shouldEnterDirectory(relPath, trackedPaths),\n includeFile: (relPath) => rules.shouldIncludeWorkingFile(relPath, trackedPaths),\n includeDirectory: (relPath, info) =>\n info.empty && rules.shouldIncludeEmptyDirectory(relPath, trackedPaths),\n });\n\n for (const entry of entries) {\n if (entry.kind === \"directory\") {\n manifest[entry.path] = { kind: \"directory\", size: 0 };\n continue;\n }\n\n const fullPath = fs.resolve(rootPath, entry.path);\n const stat = await fs.stat(fullPath);\n const cached = options.indexEntries?.[entry.path];\n\n if (\n cached &&\n cached.size === stat.size &&\n cached.mtimeMs === stat.mtimeMs &&\n await options.objectStore.hasBlob(cached.blobId)\n ) {\n const sampleHash = hashSample(await readStreamSample(fs.readStream(fullPath)));\n if (sampleHash === cached.sampleHash) {\n manifest[entry.path] = {\n kind: \"file\",\n blobId: cached.blobId,\n size: cached.size,\n };\n nextIndexEntries[entry.path] = cached;\n continue;\n }\n }\n\n const stored = await options.objectStore.store(fs.readStream(fullPath));\n manifest[entry.path] = {\n kind: \"file\",\n blobId: stored.blobId,\n size: stored.size,\n };\n nextIndexEntries[entry.path] = {\n blobId: stored.blobId,\n size: stored.size,\n mtimeMs: stat.mtimeMs,\n binary: stored.binary,\n sampleHash: stored.sampleHash,\n };\n }\n\n return { manifest, indexEntries: nextIndexEntries };\n}\n\n/**\n * Restore a working tree from a TreeManifest.\n *\n * If `fullRestore` is true, deletes working tree files not in the manifest.\n * If `paths` is provided, only restores matching files.\n */\nexport async function restoreTree(\n fs: VirtualFS,\n rootPath: string,\n manifest: TreeManifest,\n objectStore: VCSObjectStore,\n options?: {\n fullRestore?: boolean;\n paths?: string[];\n rules?: VCSRules;\n trackedPaths?: Iterable<string>;\n },\n): Promise<void> {\n const fullRestore = options?.fullRestore ?? false;\n const rules = options?.rules ?? new VCSRules({ internalDirName: \".vcs\" });\n const trackedPaths = new Set(options?.trackedPaths ?? []);\n const scopePatterns = options?.paths ?? null;\n const scopedEntries = Object.entries(manifest)\n .filter(([relPath]) => isPathInScope(relPath, scopePatterns))\n .sort(([a], [b]) => a.localeCompare(b));\n const targetPaths = new Set(scopedEntries.map(([relPath]) => relPath));\n const requiredDirectories = collectRequiredDirectories(scopedEntries);\n const shouldDeleteExtras = fullRestore || scopePatterns !== null;\n\n if (shouldDeleteExtras) {\n const currentEntries = await walkTreeEntries(fs, rootPath, {\n enterDirectory: (relPath) => !rules.isInternalPath(relPath),\n includeFile: (relPath) => rules.shouldIncludeRestoreScanFile(relPath),\n includeDirectory: () => true,\n });\n\n for (const current of currentEntries) {\n if (current.kind !== \"file\") continue;\n if (!isPathInScope(current.path, scopePatterns)) continue;\n if (targetPaths.has(current.path)) continue;\n if (rules.shouldPreserveUntrackedIgnored(current.path, trackedPaths)) continue;\n await fs.rm(fs.resolve(rootPath, current.path));\n }\n }\n\n for (const directory of [...requiredDirectories].sort(comparePathDepth)) {\n await ensureDirectoryExists(fs, fs.resolve(rootPath, directory));\n }\n\n for (const [relPath, entry] of scopedEntries) {\n if (isDirectoryEntry(entry)) {\n await ensureDirectoryExists(fs, fs.resolve(rootPath, relPath));\n continue;\n }\n await writeFileFromEntry(fs, rootPath, relPath, entry, objectStore);\n }\n\n if (shouldDeleteExtras) {\n const currentEntries = await walkTreeEntries(fs, rootPath, {\n enterDirectory: (relPath) => !rules.isInternalPath(relPath),\n includeFile: () => true,\n includeDirectory: () => true,\n });\n\n const directories = currentEntries\n .filter((entry) => entry.kind === \"directory\")\n .map((entry) => entry.path)\n .filter((relPath) => isPathInScope(relPath, scopePatterns))\n .sort((a, b) => comparePathDepth(b, a));\n\n for (const relPath of directories) {\n if (requiredDirectories.has(relPath)) continue;\n if (rules.shouldPreserveUntrackedIgnored(relPath, trackedPaths)) continue;\n const fullPath = fs.resolve(rootPath, relPath);\n if (await isEmptyDirectory(fs, fullPath)) {\n await fs.rm(fullPath);\n }\n }\n }\n}\n\nexport async function rebuildIndexForManifest(\n fs: VirtualFS,\n rootPath: string,\n manifest: TreeManifest,\n objectStore: VCSObjectStore,\n): Promise<Record<string, VCSIndexEntry>> {\n const entries: Record<string, VCSIndexEntry> = {};\n\n for (const [relPath, entry] of Object.entries(manifest)) {\n if (isDirectoryEntry(entry)) {\n continue;\n }\n\n const fullPath = fs.resolve(rootPath, relPath);\n const stat = await fs.stat(fullPath);\n entries[relPath] = {\n blobId: entry.blobId,\n size: entry.size,\n mtimeMs: stat.mtimeMs,\n binary: await objectStore.isBinaryBlob(entry.blobId),\n sampleHash: hashSample(await readStreamSample(fs.readStream(fullPath))),\n };\n }\n\n return entries;\n}\n\nexport async function updateIndexForScopedPaths(\n fs: VirtualFS,\n rootPath: string,\n manifest: TreeManifest,\n objectStore: VCSObjectStore,\n existingIndex: Record<string, VCSIndexEntry>,\n patterns: string[],\n): Promise<Record<string, VCSIndexEntry>> {\n const nextIndex = { ...existingIndex };\n\n for (const relPath of Object.keys(existingIndex)) {\n if (isPathInScope(relPath, patterns) && !manifest[relPath]) {\n delete nextIndex[relPath];\n }\n }\n\n for (const [relPath, entry] of Object.entries(manifest)) {\n if (!isPathInScope(relPath, patterns) || isDirectoryEntry(entry)) {\n continue;\n }\n\n const fullPath = fs.resolve(rootPath, relPath);\n if (!(await fs.exists(fullPath))) {\n delete nextIndex[relPath];\n continue;\n }\n const stat = await fs.stat(fullPath);\n nextIndex[relPath] = {\n blobId: entry.blobId,\n size: entry.size,\n mtimeMs: stat.mtimeMs,\n binary: await objectStore.isBinaryBlob(entry.blobId),\n sampleHash: hashSample(await readStreamSample(fs.readStream(fullPath))),\n };\n }\n\n return nextIndex;\n}\n\nasync function writeFileFromEntry(\n fs: VirtualFS,\n rootPath: string,\n relPath: string,\n entry: FileEntry,\n objectStore: VCSObjectStore,\n): Promise<void> {\n const fullPath = fs.resolve(rootPath, relPath);\n await ensureDirectoryExists(fs, fs.dirname(fullPath));\n await removeDirectoryAtPath(fs, fullPath);\n\n const writer = await fs.writeStream(fullPath);\n try {\n for await (const chunk of objectStore.readBlobStream(entry.blobId)) {\n await writer.write(chunk);\n }\n await writer.close();\n } catch (error) {\n await writer.abort?.(error);\n throw error;\n }\n}\n\nfunction isDirectoryEntry(entry: TreeEntry): entry is Extract<TreeEntry, { kind: \"directory\" }> {\n return entry.kind === \"directory\";\n}\n\nfunction isPathInScope(relPath: string, patterns: string[] | null): boolean {\n if (!patterns || patterns.length === 0) return true;\n return patterns.some((pattern) => matchVCSPath(pattern, relPath));\n}\n\nfunction collectRequiredDirectories(entries: Array<[string, TreeEntry]>): Set<string> {\n const directories = new Set<string>();\n\n for (const [relPath, entry] of entries) {\n if (isDirectoryEntry(entry)) {\n directories.add(relPath);\n }\n for (const parent of parentDirectories(relPath)) {\n directories.add(parent);\n }\n }\n\n return directories;\n}\n\nfunction parentDirectories(relPath: string): string[] {\n const parts = relPath.split(\"/\").filter(Boolean);\n const parents: string[] = [];\n\n for (let i = 1; i < parts.length; i++) {\n parents.push(parts.slice(0, i).join(\"/\"));\n }\n\n return parents;\n}\n\nfunction comparePathDepth(a: string, b: string): number {\n const depthA = a.split(\"/\").filter(Boolean).length;\n const depthB = b.split(\"/\").filter(Boolean).length;\n if (depthA !== depthB) return depthA - depthB;\n return a.localeCompare(b);\n}\n\nasync function ensureDirectoryExists(fs: VirtualFS, dirPath: string): Promise<void> {\n const parent = fs.dirname(dirPath);\n if (parent !== dirPath) {\n await ensureDirectoryExists(fs, parent);\n }\n\n if (await fs.exists(dirPath)) {\n const stat = await fs.stat(dirPath);\n if (stat.isDirectory()) return;\n await fs.rm(dirPath, { recursive: true, force: true });\n }\n\n await fs.mkdir(dirPath, { recursive: true });\n}\n\nasync function removeDirectoryAtPath(fs: VirtualFS, path: string): Promise<void> {\n if (!(await fs.exists(path))) return;\n const stat = await fs.stat(path);\n if (stat.isDirectory()) {\n await fs.rm(path, { recursive: true, force: true });\n }\n}\n\nasync function isEmptyDirectory(fs: VirtualFS, dirPath: string): Promise<boolean> {\n try {\n const entries = await fs.readdir(dirPath);\n return entries.length === 0;\n } catch {\n return false;\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "7BF3A16CF496D89964756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -42,6 +42,7 @@ __export(exports_storage, {
|
|
|
42
42
|
VCSStorage: () => VCSStorage
|
|
43
43
|
});
|
|
44
44
|
module.exports = __toCommonJS(exports_storage);
|
|
45
|
+
var VCS_FORMAT_VERSION = 2;
|
|
45
46
|
|
|
46
47
|
class VCSStorage {
|
|
47
48
|
fs;
|
|
@@ -60,9 +61,18 @@ class VCSStorage {
|
|
|
60
61
|
await this.fs.mkdir(this.basePath, { recursive: true });
|
|
61
62
|
await this.fs.mkdir(this.path("refs", "heads"), { recursive: true });
|
|
62
63
|
await this.fs.mkdir(this.path("revisions"), { recursive: true });
|
|
64
|
+
await this.fs.mkdir(this.path("objects", "blobs"), { recursive: true });
|
|
65
|
+
await this.fs.mkdir(this.path("tmp"), { recursive: true });
|
|
63
66
|
await this.writeHead({ ref: `refs/heads/${defaultBranch}` });
|
|
64
67
|
await this.writeJSON(["counter.json"], { next: 1 });
|
|
65
|
-
await this.writeJSON(["config.json"], {
|
|
68
|
+
await this.writeJSON(["config.json"], {
|
|
69
|
+
version: VCS_FORMAT_VERSION,
|
|
70
|
+
defaultBranch
|
|
71
|
+
});
|
|
72
|
+
await this.writeJSON(["index.json"], {
|
|
73
|
+
version: VCS_FORMAT_VERSION,
|
|
74
|
+
entries: {}
|
|
75
|
+
});
|
|
66
76
|
}
|
|
67
77
|
async readHead() {
|
|
68
78
|
return this.readJSON("HEAD");
|
|
@@ -104,7 +114,33 @@ class VCSStorage {
|
|
|
104
114
|
return id;
|
|
105
115
|
}
|
|
106
116
|
async readConfig() {
|
|
107
|
-
|
|
117
|
+
const config = await this.readJSON("config.json");
|
|
118
|
+
this.assertSupportedVersion(config.version);
|
|
119
|
+
return config;
|
|
120
|
+
}
|
|
121
|
+
async assertSupportedFormat() {
|
|
122
|
+
await this.readConfig();
|
|
123
|
+
}
|
|
124
|
+
async readIndex() {
|
|
125
|
+
const indexPath = this.path("index.json");
|
|
126
|
+
if (!await this.fs.exists(indexPath)) {
|
|
127
|
+
return {};
|
|
128
|
+
}
|
|
129
|
+
const index = await this.readJSON("index.json");
|
|
130
|
+
this.assertSupportedVersion(index.version);
|
|
131
|
+
return index.entries;
|
|
132
|
+
}
|
|
133
|
+
async writeIndex(entries) {
|
|
134
|
+
await this.writeJSON(["index.json"], {
|
|
135
|
+
version: VCS_FORMAT_VERSION,
|
|
136
|
+
entries
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
resolve(...segments) {
|
|
140
|
+
return this.path(...segments);
|
|
141
|
+
}
|
|
142
|
+
get fileSystem() {
|
|
143
|
+
return this.fs;
|
|
108
144
|
}
|
|
109
145
|
async readJSON(...segments) {
|
|
110
146
|
const filePath = this.path(...segments);
|
|
@@ -115,6 +151,11 @@ class VCSStorage {
|
|
|
115
151
|
const filePath = this.path(...segments);
|
|
116
152
|
await this.fs.writeFile(filePath, JSON.stringify(data, null, 2));
|
|
117
153
|
}
|
|
154
|
+
assertSupportedVersion(version) {
|
|
155
|
+
if (version !== VCS_FORMAT_VERSION) {
|
|
156
|
+
throw new Error(`unsupported VCS format version ${version ?? "unknown"}; reinitialize the repository`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
118
159
|
}
|
|
119
160
|
|
|
120
|
-
//# debugId=
|
|
161
|
+
//# debugId=CD15C789E175086364756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/vcs/storage.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { VirtualFS } from \"../types.cjs\";\nimport type {\n HeadRef,\n BranchRef,\n RevisionCounter,\n VCSConfigFile,\n Revision,\n} from \"./types.cjs\";\n\nexport class VCSStorage {\n constructor(\n private readonly fs: VirtualFS,\n private readonly basePath: string,\n ) {}\n\n private path(...segments: string[]): string {\n return this.fs.resolve(this.basePath, ...segments);\n }\n\n // --- Init ---\n\n async isInitialized(): Promise<boolean> {\n return this.fs.exists(this.path(\"HEAD\"));\n }\n\n async initialize(defaultBranch: string = \"main\"): Promise<void> {\n await this.fs.mkdir(this.basePath, { recursive: true });\n await this.fs.mkdir(this.path(\"refs\", \"heads\"), { recursive: true });\n await this.fs.mkdir(this.path(\"revisions\"), { recursive: true });\n\n await this.writeHead({ ref: `refs/heads/${defaultBranch}` });\n await this.writeJSON([\"counter.json\"], { next: 1 } satisfies RevisionCounter);\n await this.writeJSON([\"config.json\"], { defaultBranch
|
|
5
|
+
"import type { VirtualFS } from \"../types.cjs\";\nimport type {\n HeadRef,\n BranchRef,\n RevisionCounter,\n VCSConfigFile,\n VCSIndexEntry,\n VCSIndexFile,\n Revision,\n} from \"./types.cjs\";\n\nconst VCS_FORMAT_VERSION = 2;\n\nexport class VCSStorage {\n constructor(\n private readonly fs: VirtualFS,\n private readonly basePath: string,\n ) {}\n\n private path(...segments: string[]): string {\n return this.fs.resolve(this.basePath, ...segments);\n }\n\n // --- Init ---\n\n async isInitialized(): Promise<boolean> {\n return this.fs.exists(this.path(\"HEAD\"));\n }\n\n async initialize(defaultBranch: string = \"main\"): Promise<void> {\n await this.fs.mkdir(this.basePath, { recursive: true });\n await this.fs.mkdir(this.path(\"refs\", \"heads\"), { recursive: true });\n await this.fs.mkdir(this.path(\"revisions\"), { recursive: true });\n await this.fs.mkdir(this.path(\"objects\", \"blobs\"), { recursive: true });\n await this.fs.mkdir(this.path(\"tmp\"), { recursive: true });\n\n await this.writeHead({ ref: `refs/heads/${defaultBranch}` });\n await this.writeJSON([\"counter.json\"], { next: 1 } satisfies RevisionCounter);\n await this.writeJSON([\"config.json\"], {\n version: VCS_FORMAT_VERSION,\n defaultBranch,\n } satisfies VCSConfigFile);\n await this.writeJSON([\"index.json\"], {\n version: VCS_FORMAT_VERSION,\n entries: {},\n } satisfies VCSIndexFile);\n }\n\n // --- HEAD ---\n\n async readHead(): Promise<HeadRef> {\n return this.readJSON<HeadRef>(\"HEAD\");\n }\n\n async writeHead(head: HeadRef): Promise<void> {\n await this.writeJSON([\"HEAD\"], head);\n }\n\n // --- Branches ---\n\n async readBranch(name: string): Promise<BranchRef | null> {\n const branchPath = this.path(\"refs\", \"heads\", name);\n if (!(await this.fs.exists(branchPath))) return null;\n return this.readJSON<BranchRef>(\"refs\", \"heads\", name);\n }\n\n async writeBranch(name: string, ref: BranchRef): Promise<void> {\n await this.writeJSON([\"refs\", \"heads\", name], ref);\n }\n\n async deleteBranch(name: string): Promise<void> {\n const branchPath = this.path(\"refs\", \"heads\", name);\n await this.fs.rm(branchPath);\n }\n\n async listBranches(): Promise<string[]> {\n const headsPath = this.path(\"refs\", \"heads\");\n try {\n return await this.fs.readdir(headsPath);\n } catch {\n return [];\n }\n }\n\n // --- Revisions ---\n\n async readRevision(id: number): Promise<Revision> {\n return this.readJSON<Revision>(\"revisions\", `${id}.json`);\n }\n\n async writeRevision(rev: Revision): Promise<void> {\n await this.writeJSON([\"revisions\", `${rev.id}.json`], rev);\n }\n\n // --- Counter ---\n\n async nextRevisionId(): Promise<number> {\n const counter = await this.readJSON<RevisionCounter>(\"counter.json\");\n const id = counter.next;\n await this.writeJSON([\"counter.json\"], { next: id + 1 } satisfies RevisionCounter);\n return id;\n }\n\n // --- Config ---\n\n async readConfig(): Promise<VCSConfigFile> {\n const config = await this.readJSON<VCSConfigFile>(\"config.json\");\n this.assertSupportedVersion(config.version);\n return config;\n }\n\n async assertSupportedFormat(): Promise<void> {\n await this.readConfig();\n }\n\n async readIndex(): Promise<Record<string, VCSIndexEntry>> {\n const indexPath = this.path(\"index.json\");\n if (!(await this.fs.exists(indexPath))) {\n return {};\n }\n const index = await this.readJSON<VCSIndexFile>(\"index.json\");\n this.assertSupportedVersion(index.version);\n return index.entries;\n }\n\n async writeIndex(entries: Record<string, VCSIndexEntry>): Promise<void> {\n await this.writeJSON([\"index.json\"], {\n version: VCS_FORMAT_VERSION,\n entries,\n } satisfies VCSIndexFile);\n }\n\n resolve(...segments: string[]): string {\n return this.path(...segments);\n }\n\n get fileSystem(): VirtualFS {\n return this.fs;\n }\n\n // --- Helpers ---\n\n private async readJSON<T>(...segments: string[]): Promise<T> {\n const filePath = this.path(...segments);\n const content = await this.fs.readFile(filePath, \"utf8\");\n return JSON.parse(content) as T;\n }\n\n private async writeJSON(segments: string[], data: unknown): Promise<void> {\n const filePath = this.path(...segments);\n await this.fs.writeFile(filePath, JSON.stringify(data, null, 2));\n }\n\n private assertSupportedVersion(version: number | undefined): void {\n if (version !== VCS_FORMAT_VERSION) {\n throw new Error(\n `unsupported VCS format version ${version ?? \"unknown\"}; reinitialize the repository`,\n );\n }\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "CD15C789E175086364756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
function __accessProp(key) {
|
|
6
|
+
return this[key];
|
|
7
|
+
}
|
|
8
|
+
var __toCommonJS = (from) => {
|
|
9
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
10
|
+
if (entry)
|
|
11
|
+
return entry;
|
|
12
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (var key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(entry, key))
|
|
16
|
+
__defProp(entry, key, {
|
|
17
|
+
get: __accessProp.bind(from, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
__moduleCache.set(from, entry);
|
|
22
|
+
return entry;
|
|
23
|
+
};
|
|
24
|
+
var __moduleCache;
|
|
25
|
+
var __returnValue = (v) => v;
|
|
26
|
+
function __exportSetter(name, newValue) {
|
|
27
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
28
|
+
}
|
|
29
|
+
var __export = (target, all) => {
|
|
30
|
+
for (var name in all)
|
|
31
|
+
__defProp(target, name, {
|
|
32
|
+
get: all[name],
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
set: __exportSetter.bind(all, name)
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/vcs/text-diff.ts
|
|
40
|
+
var exports_text_diff = {};
|
|
41
|
+
__export(exports_text_diff, {
|
|
42
|
+
createUnifiedPatch: () => createUnifiedPatch
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(exports_text_diff);
|
|
45
|
+
var CONTEXT_LINES = 3;
|
|
46
|
+
function createUnifiedPatch(path, previousText, nextText) {
|
|
47
|
+
if (previousText === nextText) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const previousLines = splitLines(previousText);
|
|
51
|
+
const nextLines = splitLines(nextText);
|
|
52
|
+
const operations = diffLines(previousLines, nextLines);
|
|
53
|
+
const hunks = collectHunks(operations);
|
|
54
|
+
if (hunks.length === 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const lines = [`--- a/${path}`, `+++ b/${path}`];
|
|
58
|
+
for (const hunk of hunks) {
|
|
59
|
+
lines.push(formatHunkHeader(operations, hunk));
|
|
60
|
+
for (let i = hunk.start;i < hunk.end; i++) {
|
|
61
|
+
const operation = operations[i];
|
|
62
|
+
const prefix = operation.type === "equal" ? " " : operation.type === "delete" ? "-" : "+";
|
|
63
|
+
lines.push(prefix + operation.line);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return lines.join(`
|
|
67
|
+
`);
|
|
68
|
+
}
|
|
69
|
+
function splitLines(text) {
|
|
70
|
+
if (!text) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const normalized = text.replace(/\r\n/g, `
|
|
74
|
+
`);
|
|
75
|
+
const parts = normalized.split(`
|
|
76
|
+
`);
|
|
77
|
+
if (normalized.endsWith(`
|
|
78
|
+
`)) {
|
|
79
|
+
parts.pop();
|
|
80
|
+
}
|
|
81
|
+
return parts;
|
|
82
|
+
}
|
|
83
|
+
function diffLines(previousLines, nextLines) {
|
|
84
|
+
const previousCount = previousLines.length;
|
|
85
|
+
const nextCount = nextLines.length;
|
|
86
|
+
const max = previousCount + nextCount;
|
|
87
|
+
const trace = [];
|
|
88
|
+
let frontier = new Map;
|
|
89
|
+
frontier.set(1, 0);
|
|
90
|
+
for (let depth = 0;depth <= max; depth++) {
|
|
91
|
+
trace.push(new Map(frontier));
|
|
92
|
+
for (let diagonal = -depth;diagonal <= depth; diagonal += 2) {
|
|
93
|
+
const nextX = frontier.get(diagonal + 1) ?? 0;
|
|
94
|
+
const prevX = frontier.get(diagonal - 1) ?? 0;
|
|
95
|
+
let x;
|
|
96
|
+
if (diagonal === -depth || diagonal !== depth && prevX < nextX) {
|
|
97
|
+
x = nextX;
|
|
98
|
+
} else {
|
|
99
|
+
x = prevX + 1;
|
|
100
|
+
}
|
|
101
|
+
let y = x - diagonal;
|
|
102
|
+
while (x < previousCount && y < nextCount && previousLines[x] === nextLines[y]) {
|
|
103
|
+
x++;
|
|
104
|
+
y++;
|
|
105
|
+
}
|
|
106
|
+
frontier.set(diagonal, x);
|
|
107
|
+
if (x >= previousCount && y >= nextCount) {
|
|
108
|
+
return backtrack(trace, previousLines, nextLines);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
function backtrack(trace, previousLines, nextLines) {
|
|
115
|
+
const operations = [];
|
|
116
|
+
let x = previousLines.length;
|
|
117
|
+
let y = nextLines.length;
|
|
118
|
+
for (let depth = trace.length - 1;depth >= 0; depth--) {
|
|
119
|
+
const frontier = trace[depth];
|
|
120
|
+
const diagonal = x - y;
|
|
121
|
+
let previousDiagonal;
|
|
122
|
+
if (diagonal === -depth || diagonal !== depth && (frontier.get(diagonal - 1) ?? 0) < (frontier.get(diagonal + 1) ?? 0)) {
|
|
123
|
+
previousDiagonal = diagonal + 1;
|
|
124
|
+
} else {
|
|
125
|
+
previousDiagonal = diagonal - 1;
|
|
126
|
+
}
|
|
127
|
+
const previousX = frontier.get(previousDiagonal) ?? 0;
|
|
128
|
+
const previousY = previousX - previousDiagonal;
|
|
129
|
+
while (x > previousX && y > previousY) {
|
|
130
|
+
operations.push({ type: "equal", line: previousLines[x - 1] });
|
|
131
|
+
x--;
|
|
132
|
+
y--;
|
|
133
|
+
}
|
|
134
|
+
if (depth === 0) {
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
if (x === previousX) {
|
|
138
|
+
operations.push({ type: "insert", line: nextLines[y - 1] });
|
|
139
|
+
y--;
|
|
140
|
+
} else {
|
|
141
|
+
operations.push({ type: "delete", line: previousLines[x - 1] });
|
|
142
|
+
x--;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
while (x > 0 && y > 0) {
|
|
146
|
+
operations.push({ type: "equal", line: previousLines[x - 1] });
|
|
147
|
+
x--;
|
|
148
|
+
y--;
|
|
149
|
+
}
|
|
150
|
+
while (x > 0) {
|
|
151
|
+
operations.push({ type: "delete", line: previousLines[x - 1] });
|
|
152
|
+
x--;
|
|
153
|
+
}
|
|
154
|
+
while (y > 0) {
|
|
155
|
+
operations.push({ type: "insert", line: nextLines[y - 1] });
|
|
156
|
+
y--;
|
|
157
|
+
}
|
|
158
|
+
return operations.reverse();
|
|
159
|
+
}
|
|
160
|
+
function collectHunks(operations) {
|
|
161
|
+
const changeRanges = [];
|
|
162
|
+
let rangeStart = null;
|
|
163
|
+
for (let index = 0;index < operations.length; index++) {
|
|
164
|
+
if (operations[index].type === "equal") {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (rangeStart === null) {
|
|
168
|
+
rangeStart = Math.max(0, index - CONTEXT_LINES);
|
|
169
|
+
}
|
|
170
|
+
let rangeEnd = Math.min(operations.length, index + CONTEXT_LINES + 1);
|
|
171
|
+
while (rangeEnd < operations.length && operations.slice(index + 1, rangeEnd).some((op) => op.type !== "equal")) {
|
|
172
|
+
rangeEnd = Math.min(operations.length, rangeEnd + CONTEXT_LINES);
|
|
173
|
+
}
|
|
174
|
+
const previousRange = changeRanges[changeRanges.length - 1];
|
|
175
|
+
if (previousRange && rangeStart <= previousRange.end) {
|
|
176
|
+
previousRange.end = Math.max(previousRange.end, rangeEnd);
|
|
177
|
+
} else {
|
|
178
|
+
changeRanges.push({ start: rangeStart, end: rangeEnd });
|
|
179
|
+
}
|
|
180
|
+
rangeStart = null;
|
|
181
|
+
}
|
|
182
|
+
return changeRanges;
|
|
183
|
+
}
|
|
184
|
+
function formatHunkHeader(operations, hunk) {
|
|
185
|
+
let oldStart = 1;
|
|
186
|
+
let newStart = 1;
|
|
187
|
+
for (let index = 0;index < hunk.start; index++) {
|
|
188
|
+
const operation = operations[index];
|
|
189
|
+
if (operation.type !== "insert") {
|
|
190
|
+
oldStart++;
|
|
191
|
+
}
|
|
192
|
+
if (operation.type !== "delete") {
|
|
193
|
+
newStart++;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
let oldCount = 0;
|
|
197
|
+
let newCount = 0;
|
|
198
|
+
for (let index = hunk.start;index < hunk.end; index++) {
|
|
199
|
+
const operation = operations[index];
|
|
200
|
+
if (operation.type !== "insert") {
|
|
201
|
+
oldCount++;
|
|
202
|
+
}
|
|
203
|
+
if (operation.type !== "delete") {
|
|
204
|
+
newCount++;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return `@@ -${formatRange(oldStart, oldCount)} +${formatRange(newStart, newCount)} @@`;
|
|
208
|
+
}
|
|
209
|
+
function formatRange(start, count) {
|
|
210
|
+
if (count === 0) {
|
|
211
|
+
return `${start - 1},0`;
|
|
212
|
+
}
|
|
213
|
+
if (count === 1) {
|
|
214
|
+
return String(start);
|
|
215
|
+
}
|
|
216
|
+
return `${start},${count}`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
//# debugId=D08732997986015F64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/vcs/text-diff.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"interface DiffOp {\n type: \"equal\" | \"insert\" | \"delete\";\n line: string;\n}\n\ninterface HunkRange {\n start: number;\n end: number;\n}\n\nconst CONTEXT_LINES = 3;\n\nexport function createUnifiedPatch(path: string, previousText: string, nextText: string): string | undefined {\n if (previousText === nextText) {\n return undefined;\n }\n\n const previousLines = splitLines(previousText);\n const nextLines = splitLines(nextText);\n const operations = diffLines(previousLines, nextLines);\n const hunks = collectHunks(operations);\n\n if (hunks.length === 0) {\n return undefined;\n }\n\n const lines = [`--- a/${path}`, `+++ b/${path}`];\n for (const hunk of hunks) {\n lines.push(formatHunkHeader(operations, hunk));\n for (let i = hunk.start; i < hunk.end; i++) {\n const operation = operations[i]!;\n const prefix =\n operation.type === \"equal\" ? \" \" : operation.type === \"delete\" ? \"-\" : \"+\";\n lines.push(prefix + operation.line);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction splitLines(text: string): string[] {\n if (!text) {\n return [];\n }\n const normalized = text.replace(/\\r\\n/g, \"\\n\");\n const parts = normalized.split(\"\\n\");\n if (normalized.endsWith(\"\\n\")) {\n parts.pop();\n }\n return parts;\n}\n\nfunction diffLines(previousLines: string[], nextLines: string[]): DiffOp[] {\n const previousCount = previousLines.length;\n const nextCount = nextLines.length;\n const max = previousCount + nextCount;\n const trace: Map<number, number>[] = [];\n let frontier = new Map<number, number>();\n frontier.set(1, 0);\n\n for (let depth = 0; depth <= max; depth++) {\n trace.push(new Map(frontier));\n\n for (let diagonal = -depth; diagonal <= depth; diagonal += 2) {\n const nextX = frontier.get(diagonal + 1) ?? 0;\n const prevX = frontier.get(diagonal - 1) ?? 0;\n let x: number;\n\n if (diagonal === -depth || (diagonal !== depth && prevX < nextX)) {\n x = nextX;\n } else {\n x = prevX + 1;\n }\n\n let y = x - diagonal;\n while (\n x < previousCount &&\n y < nextCount &&\n previousLines[x] === nextLines[y]\n ) {\n x++;\n y++;\n }\n\n frontier.set(diagonal, x);\n\n if (x >= previousCount && y >= nextCount) {\n return backtrack(trace, previousLines, nextLines);\n }\n }\n }\n\n return [];\n}\n\nfunction backtrack(\n trace: Map<number, number>[],\n previousLines: string[],\n nextLines: string[],\n): DiffOp[] {\n const operations: DiffOp[] = [];\n let x = previousLines.length;\n let y = nextLines.length;\n\n for (let depth = trace.length - 1; depth >= 0; depth--) {\n const frontier = trace[depth]!;\n const diagonal = x - y;\n\n let previousDiagonal: number;\n if (\n diagonal === -depth ||\n (diagonal !== depth && (frontier.get(diagonal - 1) ?? 0) < (frontier.get(diagonal + 1) ?? 0))\n ) {\n previousDiagonal = diagonal + 1;\n } else {\n previousDiagonal = diagonal - 1;\n }\n\n const previousX = frontier.get(previousDiagonal) ?? 0;\n const previousY = previousX - previousDiagonal;\n\n while (x > previousX && y > previousY) {\n operations.push({ type: \"equal\", line: previousLines[x - 1]! });\n x--;\n y--;\n }\n\n if (depth === 0) {\n break;\n }\n\n if (x === previousX) {\n operations.push({ type: \"insert\", line: nextLines[y - 1]! });\n y--;\n } else {\n operations.push({ type: \"delete\", line: previousLines[x - 1]! });\n x--;\n }\n }\n\n while (x > 0 && y > 0) {\n operations.push({ type: \"equal\", line: previousLines[x - 1]! });\n x--;\n y--;\n }\n while (x > 0) {\n operations.push({ type: \"delete\", line: previousLines[x - 1]! });\n x--;\n }\n while (y > 0) {\n operations.push({ type: \"insert\", line: nextLines[y - 1]! });\n y--;\n }\n\n return operations.reverse();\n}\n\nfunction collectHunks(operations: DiffOp[]): HunkRange[] {\n const changeRanges: HunkRange[] = [];\n let rangeStart: number | null = null;\n\n for (let index = 0; index < operations.length; index++) {\n if (operations[index]!.type === \"equal\") {\n continue;\n }\n if (rangeStart === null) {\n rangeStart = Math.max(0, index - CONTEXT_LINES);\n }\n let rangeEnd = Math.min(operations.length, index + CONTEXT_LINES + 1);\n\n while (\n rangeEnd < operations.length &&\n operations.slice(index + 1, rangeEnd).some((op) => op.type !== \"equal\")\n ) {\n rangeEnd = Math.min(operations.length, rangeEnd + CONTEXT_LINES);\n }\n\n const previousRange = changeRanges[changeRanges.length - 1];\n if (previousRange && rangeStart <= previousRange.end) {\n previousRange.end = Math.max(previousRange.end, rangeEnd);\n } else {\n changeRanges.push({ start: rangeStart, end: rangeEnd });\n }\n rangeStart = null;\n }\n\n return changeRanges;\n}\n\nfunction formatHunkHeader(operations: DiffOp[], hunk: HunkRange): string {\n let oldStart = 1;\n let newStart = 1;\n\n for (let index = 0; index < hunk.start; index++) {\n const operation = operations[index]!;\n if (operation.type !== \"insert\") {\n oldStart++;\n }\n if (operation.type !== \"delete\") {\n newStart++;\n }\n }\n\n let oldCount = 0;\n let newCount = 0;\n for (let index = hunk.start; index < hunk.end; index++) {\n const operation = operations[index]!;\n if (operation.type !== \"insert\") {\n oldCount++;\n }\n if (operation.type !== \"delete\") {\n newCount++;\n }\n }\n\n return `@@ -${formatRange(oldStart, oldCount)} +${formatRange(newStart, newCount)} @@`;\n}\n\nfunction formatRange(start: number, count: number): string {\n if (count === 0) {\n return `${start - 1},0`;\n }\n if (count === 1) {\n return String(start);\n }\n return `${start},${count}`;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "D08732997986015F64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|