| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | | // Utilities for spawning rsync and consuming its output as a `Progress` |
| 1 | // Utilities for spawning rsync and consuming its output as a `progress.Node` |
| 2 | 2 | // A headless parser is available with `Parse` |
| 3 | 3 | |
| 4 | 4 | export type Line = |
| ... | ... | @@ -37,6 +37,8 @@ export async function spawn(options: SpawnOptions) { |
| 37 | 37 | }); |
| 38 | 38 | const parser = new Parse(); |
| 39 | 39 | let running = true; |
| 40 | let fileNode: progress.Node | null = null; |
| 41 | let fileNodeTransferNumber = 0; |
| 40 | 42 | |
| 41 | 43 | const stdoutSplitter = readline.createInterface({ input: proc.stdout }); |
| 42 | 44 | const stderrSplitter = readline.createInterface({ input: proc.stderr }); |
| ... | ... | @@ -51,7 +53,7 @@ export async function spawn(options: SpawnOptions) { |
| 51 | 53 | node.log[result.level](result.message); |
| 52 | 54 | } else if (result.kind === "count") { |
| 53 | 55 | if (!running) return; |
| 54 | | node.text = `${title} (${result.files} files...)`; |
| 56 | node.total = result.files; |
| 55 | 57 | } else if (result.kind === "progress") { |
| 56 | 58 | if (!running) return; |
| 57 | 59 | const { |
| ... | ... | @@ -64,17 +66,23 @@ export async function spawn(options: SpawnOptions) { |
| 64 | 66 | } = result; |
| 65 | 67 | node.value = transferNumber; |
| 66 | 68 | node.total = totalFiles; |
| 67 | | const extras = [ |
| 68 | | string.formatByteSize(bytesTransferred), |
| 69 | | (totalFiles > filesToCheck) |
| 70 | | ? `${totalFiles - filesToCheck} unchecked` |
| 71 | | : null, |
| 72 | | speed, |
| 73 | | ].filter(Boolean).join(", "); |
| 69 | node.text = filesToCheck > 0 |
| 70 | ? `${title} (${filesToCheck} left)` |
| 71 | : title; |
| 74 | 72 | const fileName = currentFile.length > 20 |
| 75 | 73 | ? `${currentFile.slice(0, 3)}..${currentFile.slice(-15)}` |
| 76 | 74 | : currentFile; |
| 77 | | node.text = `${title} - ${fileName}${extras.length > 0 ? ` (${extras})` : ""}`; |
| 75 | if (!fileNode || fileNodeTransferNumber !== transferNumber) { |
| 76 | fileNode?.end(); |
| 77 | fileNode = node.start(fileName, { units: "bytes" }); |
| 78 | fileNodeTransferNumber = transferNumber; |
| 79 | } |
| 80 | const estimatedTotal = result.percentage > 0 |
| 81 | ? Math.ceil(bytesTransferred / (result.percentage / 100)) |
| 82 | : 0; |
| 83 | fileNode.text = speed ? `${fileName} (${speed})` : fileName; |
| 84 | fileNode.value = bytesTransferred; |
| 85 | fileNode.total = estimatedTotal; |
| 78 | 86 | } else result satisfies never; |
| 79 | 87 | }; |
| 80 | 88 | |
| ... | ... | @@ -83,6 +91,7 @@ export async function spawn(options: SpawnOptions) { |
| 83 | 91 | |
| 84 | 92 | const [code, signal] = await events.once(proc, "close"); |
| 85 | 93 | running = false; |
| 94 | fileNode?.end(); |
| 86 | 95 | if (code !== 0) { |
| 87 | 96 | const fmt = code ? `code ${code}` : `signal ${signal}`; |
| 88 | 97 | const e: any = new Error(`rsync failed with ${fmt}`); |
| ... | ... | @@ -119,7 +128,7 @@ export class Parse { |
| 119 | 128 | totalStr, |
| 120 | 129 | ] = progressMatch; |
| 121 | 130 | |
| 122 | | this.currentTransfer = Number(transferStr); |
| 131 | if (transferStr) this.currentTransfer = Number(transferStr); |
| 123 | 132 | |
| 124 | 133 | return { |
| 125 | 134 | kind: "progress", |
| ... | ... | @@ -181,7 +190,6 @@ export class Parse { |
| 181 | 190 | import { Path } from "#sitegen/path"; |
| 182 | 191 | import { UNWRAP } from "@clo/lib/assert"; |
| 183 | 192 | import * as progress from "@clo/lib/progress"; |
| 184 | | import * as string from "@clo/lib/string"; |
| 185 | 193 | import * as child_process from "node:child_process"; |
| 186 | 194 | import events from "node:events"; |
| 187 | 195 | import * as readline from "node:readline"; |