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