VSCodium/VS Code extension to support for Chez Scheme: Highlighting, autocompletion, documentation on hover and syntax checks.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix formatting of source file

Signed-off-by: Release-Candidate <rec@gmx.at>

+292 -278
+292 -278
generate_function_documentation.ts
··· 21 21 22 22 import * as https from "https"; 23 23 import { 24 - createWriteStream, 25 - existsSync, 26 - readFile, 27 - unlinkSync, 28 - writeFile, 24 + createWriteStream, 25 + existsSync, 26 + readFile, 27 + unlinkSync, 28 + writeFile, 29 29 } from "fs"; 30 30 import { JSDOM } from "jsdom"; 31 31 import { basename } from "path"; ··· 36 36 * with module imports. 37 37 */ 38 38 type IdentifierType = 39 - | "syntax" 40 - | "module" 41 - | "procedure" 42 - | "thread parameter" 43 - | "global parameter" 44 - | "Error: unknown"; 39 + | "syntax" 40 + | "module" 41 + | "procedure" 42 + | "thread parameter" 43 + | "global parameter" 44 + | "Error: unknown"; 45 45 46 46 /** 47 47 * Return the string `s` converted to an `IdentifierType`. ··· 51 51 * Return `"Error: unknown"` if the string isn't recognized. 52 52 */ 53 53 function stringToIdentifierType(s: string): IdentifierType { 54 - switch (s) { 55 - case "syntax": 56 - return "syntax"; 57 - case "procedure": 58 - return "procedure"; 59 - case "module": 60 - return "module"; 61 - case "thread param": 62 - return "thread parameter"; 63 - case "global param": 64 - return "global parameter"; 65 - } 54 + switch (s) { 55 + case "syntax": 56 + return "syntax"; 57 + case "procedure": 58 + return "procedure"; 59 + case "module": 60 + return "module"; 61 + case "thread param": 62 + return "thread parameter"; 63 + case "global param": 64 + return "global parameter"; 65 + } 66 66 67 - return "Error: unknown"; 67 + return "Error: unknown"; 68 68 } 69 69 70 70 /** ··· 73 73 * with module imports. 74 74 */ 75 75 type FunctionDoc = { 76 - name: string; 77 - startParen: boolean; 78 - endParen: boolean; 79 - params: string[]; 80 - type: IdentifierType; 81 - moduleNames: string[]; 82 - url: URL; 83 - description: string; 76 + name: string; 77 + startParen: boolean; 78 + endParen: boolean; 79 + params: string[]; 80 + type: IdentifierType; 81 + moduleNames: string[]; 82 + url: URL; 83 + description: string; 84 84 }; 85 85 86 86 /** ··· 101 101 * The first group contains the libraries names. 102 102 */ 103 103 const librariesRegex = 104 - /^\s*\*\*libraries:\*\*\s*(\\`\(.*?\)\\`(?:,\s*\\`\(.*?\)\\`)*)\s*<br>\s*$/mu; 104 + /^\s*\*\*libraries:\*\*\s*(\\`\(.*?\)\\`(?:,\s*\\`\(.*?\)\\`)*)\s*<br>\s*$/mu; 105 105 106 106 /** 107 107 * The base part of the Chez Scheme documentation URL. ··· 127 127 * Main entry point. 128 128 */ 129 129 async function main(): Promise<void> { 130 - try { 131 - const htmlText = await downloadAndRead(docURL); 132 - const tsText = await processHTML(htmlText); 133 - await writeFunctionDocumentation(tsText); 134 - const deleteSet = new Set(filesToDelete); 135 - deleteSet.forEach((file) => unlinkSync(file)); 136 - } catch (error) { 137 - console.error( 138 - `Caught "${error}" trying to process the HTML and saving it.` 139 - ); 140 - process.exit(1); 141 - } 130 + try { 131 + const htmlText = await downloadAndRead(docURL); 132 + const tsText = await processHTML(htmlText); 133 + await writeFunctionDocumentation(tsText); 134 + const deleteSet = new Set(filesToDelete); 135 + deleteSet.forEach((file) => unlinkSync(file)); 136 + } catch (error) { 137 + console.error( 138 + `Caught "${error}" trying to process the HTML and saving it.` 139 + ); 140 + process.exit(1); 141 + } 142 142 } 143 143 144 144 /** ··· 149 149 * text file. 150 150 */ 151 151 async function processHTML(text: string): Promise<string> { 152 - const htmlDoc = new JSDOM(text).window.document; 153 - const trs = Array.from(htmlDoc.querySelectorAll("tr")).filter( 154 - // eslint-disable-next-line no-magic-numbers 155 - (e) => e.childElementCount === 3 && e.children[0].nodeName !== "TH" 156 - ); 157 - const ids: FunctionDoc[] = trs.map((tr) => parseTR(tr)); 158 - const allUrl = Array.from( 159 - new Set( 160 - ids.map((id) => id.url.protocol + id.url.hostname + id.url.pathname) 161 - ) 162 - ); 163 - await Promise.all( 164 - allUrl.map((url) => download(new URL(url), fileNameFromURL(new URL(url)))) 165 - ); 166 - await Promise.all(ids.map((id) => addDescription(id))); 152 + const htmlDoc = new JSDOM(text).window.document; 153 + const trs = Array.from(htmlDoc.querySelectorAll("tr")).filter( 154 + // eslint-disable-next-line no-magic-numbers 155 + (e) => e.childElementCount === 3 && e.children[0].nodeName !== "TH" 156 + ); 157 + const ids: FunctionDoc[] = trs.map((tr) => parseTR(tr)); 158 + const allUrl = Array.from( 159 + new Set( 160 + ids.map((id) => id.url.protocol + id.url.hostname + id.url.pathname) 161 + ) 162 + ); 163 + await Promise.all( 164 + allUrl.map((url) => 165 + download(new URL(url), fileNameFromURL(new URL(url))) 166 + ) 167 + ); 168 + await Promise.all(ids.map((id) => addDescription(id))); 167 169 168 - ids.forEach((id) => addLibraries(id)); 170 + ids.forEach((id) => addLibraries(id)); 169 171 170 - return idsDocToTSFile(ids); 172 + return idsDocToTSFile(ids); 171 173 } 172 174 173 175 /** ··· 176 178 * @returns A TS file content with the list of `FunctionDoc`s. 177 179 */ 178 180 function idsDocToTSFile(ids: FunctionDoc[]): string { 179 - const today = new Date(); 180 - const date = today.getDate(); 181 - const month = today.getMonth() + 1; 182 - const year = today.getFullYear(); 181 + const today = new Date(); 182 + const date = today.getDate(); 183 + const month = today.getMonth() + 1; 184 + const year = today.getFullYear(); 183 185 184 - return `/* 186 + return `/* 185 187 * SPDX-FileCopyrightText: Copyright 2023 Roland Csaszar 186 188 * SPDX-License-Identifier: MIT 187 189 * ··· 203 205 204 206 export const functionDocs: FunctionDoc[] = [ 205 207 ${ids 206 - .map( 207 - (id) => 208 - ` { 208 + .map( 209 + (id) => 210 + ` { 209 211 name: "${id.name}", 210 212 startParen: ${id.startParen}, 211 213 endParen: ${id.endParen}, ··· 215 217 url: new URL("${id.url}"), 216 218 description: \`${id.description}\` 217 219 },` 218 - ) 219 - .join("\n")} 220 + ) 221 + .join("\n")} 220 222 ] 221 223 `; 222 224 } ··· 228 230 */ 229 231 // eslint-disable-next-line max-statements, max-lines-per-function 230 232 function parseTR(tr: HTMLTableRowElement): FunctionDoc { 231 - const tds = Array.from(tr.childNodes) as HTMLTableCellElement[]; 232 - const idType = stringToIdentifierType(tds[1].innerHTML); 233 - let name = ""; 234 - let params: string[] = []; 235 - let startParen = false; 236 - let endParen = false; 237 - const nameElems = tds[0].childNodes[0].childNodes; 238 - if (idType === "global parameter" || idType === "thread parameter") { 239 - const tmpName = stringOrEmpty(nameElems[0].textContent); 240 - startParen = tmpName.startsWith("("); 241 - name = startParen ? tmpName.slice(1) : tmpName; 242 - } else if (nameElems.length > 1) { 243 - ({ startParen, name, endParen } = parseParamsAndName({ 244 - nameElems, 245 - startParen, 246 - name, 247 - params, 248 - endParen, 249 - })); 250 - } else { 251 - const tmpName = stringOrEmpty(nameElems[0].textContent).trim(); 252 - startParen = tmpName.startsWith("("); 253 - endParen = tmpName.endsWith(")"); 254 - // eslint-disable-next-line no-nested-ternary 255 - name = startParen 256 - ? endParen 257 - ? tmpName.slice(1).slice(0, -1) 258 - : tmpName.slice(1) 259 - : endParen 260 - ? tmpName.slice(0, -1) 261 - : tmpName; 262 - } 263 - const url = new URL( 264 - // eslint-disable-next-line no-magic-numbers, dot-notation 265 - (tds[2].childNodes[0] as HTMLAnchorElement).href.startsWith("./") 266 - ? // eslint-disable-next-line no-magic-numbers 267 - baseURL + (tds[2].childNodes[0] as HTMLAnchorElement).href 268 - : // eslint-disable-next-line no-magic-numbers 269 - (tds[2].childNodes[0] as HTMLAnchorElement).href 270 - ); 271 - url.protocol = "https"; 272 - return { 273 - name, 274 - startParen, 275 - endParen, 276 - type: idType, 277 - moduleNames: [], 278 - params, 279 - url, 280 - description: "", 281 - }; 233 + const tds = Array.from(tr.childNodes) as HTMLTableCellElement[]; 234 + const idType = stringToIdentifierType(tds[1].innerHTML); 235 + let name = ""; 236 + let params: string[] = []; 237 + let startParen = false; 238 + let endParen = false; 239 + const nameElems = tds[0].childNodes[0].childNodes; 240 + if (idType === "global parameter" || idType === "thread parameter") { 241 + const tmpName = stringOrEmpty(nameElems[0].textContent); 242 + startParen = tmpName.startsWith("("); 243 + name = startParen ? tmpName.slice(1) : tmpName; 244 + } else if (nameElems.length > 1) { 245 + ({ startParen, name, endParen } = parseParamsAndName({ 246 + nameElems, 247 + startParen, 248 + name, 249 + params, 250 + endParen, 251 + })); 252 + } else { 253 + const tmpName = stringOrEmpty(nameElems[0].textContent).trim(); 254 + startParen = tmpName.startsWith("("); 255 + endParen = tmpName.endsWith(")"); 256 + // eslint-disable-next-line no-nested-ternary 257 + name = startParen 258 + ? endParen 259 + ? tmpName.slice(1).slice(0, -1) 260 + : tmpName.slice(1) 261 + : endParen 262 + ? tmpName.slice(0, -1) 263 + : tmpName; 264 + } 265 + const url = new URL( 266 + // eslint-disable-next-line no-magic-numbers, dot-notation 267 + (tds[2].childNodes[0] as HTMLAnchorElement).href.startsWith("./") 268 + ? // eslint-disable-next-line no-magic-numbers 269 + baseURL + (tds[2].childNodes[0] as HTMLAnchorElement).href 270 + : // eslint-disable-next-line no-magic-numbers 271 + (tds[2].childNodes[0] as HTMLAnchorElement).href 272 + ); 273 + url.protocol = "https"; 274 + return { 275 + name, 276 + startParen, 277 + endParen, 278 + type: idType, 279 + moduleNames: [], 280 + params, 281 + url, 282 + description: "", 283 + }; 282 284 } 283 285 284 286 /** ··· 290 292 */ 291 293 // eslint-disable-next-line max-statements 292 294 function parseParamsAndName(data: { 293 - nameElems: NodeListOf<ChildNode>; 294 - startParen: boolean; 295 - name: string; 296 - params: string[]; 297 - endParen: boolean; 295 + nameElems: NodeListOf<ChildNode>; 296 + startParen: boolean; 297 + name: string; 298 + params: string[]; 299 + endParen: boolean; 298 300 }): { startParen: boolean; name: string; endParen: boolean } { 299 - const tmpName = stringOrEmpty(data.nameElems[0].textContent).trimStart(); 300 - data.startParen = tmpName.startsWith("("); 301 - data.name = data.startParen ? tmpName.slice(1) : tmpName; 301 + const tmpName = stringOrEmpty(data.nameElems[0].textContent).trimStart(); 302 + data.startParen = tmpName.startsWith("("); 303 + data.name = data.startParen ? tmpName.slice(1) : tmpName; 302 304 303 - // eslint-disable-next-line no-plusplus 304 - for (let nameIdx = 1; nameIdx < data.nameElems.length - 1; nameIdx++) { 305 - if (data.nameElems[nameIdx].nodeName === "I") { 306 - data.params.push(stringOrEmpty(data.nameElems[nameIdx].textContent)); 305 + // eslint-disable-next-line no-plusplus 306 + for (let nameIdx = 1; nameIdx < data.nameElems.length - 1; nameIdx++) { 307 + if (data.nameElems[nameIdx].nodeName === "I") { 308 + data.params.push( 309 + stringOrEmpty(data.nameElems[nameIdx].textContent) 310 + ); 311 + } 307 312 } 308 - } 309 - const end = stringOrEmpty( 310 - data.nameElems[data.nameElems.length - 1].textContent 311 - ).trim(); 312 - if (end.endsWith(")")) { 313 - if (end !== ")") { 314 - data.params.push(end.slice(0, -1).trim()); 313 + const end = stringOrEmpty( 314 + data.nameElems[data.nameElems.length - 1].textContent 315 + ).trim(); 316 + if (end.endsWith(")")) { 317 + if (end !== ")") { 318 + data.params.push(end.slice(0, -1).trim()); 319 + } 320 + data.endParen = true; 315 321 } 316 - data.endParen = true; 317 - } 318 - return { 319 - startParen: data.startParen, 320 - name: data.name, 321 - endParen: data.endParen, 322 - }; 322 + return { 323 + startParen: data.startParen, 324 + name: data.name, 325 + endParen: data.endParen, 326 + }; 323 327 } 324 328 325 329 /** ··· 329 333 */ 330 334 // eslint-disable-next-line max-statements 331 335 async function addDescription(id: FunctionDoc) { 332 - const htmlString = await downloadAndRead(id.url.toString()); 333 - const htmlDoc = new JSDOM(htmlString).window.document; 334 - const anchor = id.url.hash.slice(1); 335 - let currP = htmlDoc.querySelector(`a[name="${anchor}"]`)?.closest("p"); 336 - const first = currP; 337 - const text = [""]; 338 - while ( 339 - currP && 340 - (currP === first || 341 - // eslint-disable-next-line no-eq-null, eqeqeq 342 - currP.querySelector(`a[name]:not(a[name="${anchor}"])`) == null) 343 - ) { 344 - currP.childNodes.forEach((c) => parseChildNode(c, text)); 345 - text.push("<br>\n"); 346 - currP = currP.nextElementSibling as HTMLParagraphElement; 347 - } 348 - id.description = sanitizeDescription(text.join("")); 336 + const htmlString = await downloadAndRead(id.url.toString()); 337 + const htmlDoc = new JSDOM(htmlString).window.document; 338 + const anchor = id.url.hash.slice(1); 339 + let currP = htmlDoc.querySelector(`a[name="${anchor}"]`)?.closest("p"); 340 + const first = currP; 341 + const text = [""]; 342 + while ( 343 + currP && 344 + (currP === first || 345 + // eslint-disable-next-line no-eq-null, eqeqeq 346 + currP.querySelector(`a[name]:not(a[name="${anchor}"])`) == null) 347 + ) { 348 + currP.childNodes.forEach((c) => parseChildNode(c, text)); 349 + text.push("<br>\n"); 350 + currP = currP.nextElementSibling as HTMLParagraphElement; 351 + } 352 + id.description = sanitizeDescription(text.join("")); 349 353 } 350 354 351 355 /** ··· 354 358 * @param id The `FunctionDoc` object to process. 355 359 */ 356 360 function addLibraries(id: FunctionDoc) { 357 - const match = id.description.match(librariesRegex); 358 - if (match) { 359 - // eslint-disable-next-line prefer-destructuring 360 - const librariesRaw = match[1]; 361 - const libraries = librariesRaw.replace(/\\`/gu, "").split(/,\s*/gu); 362 - id.moduleNames = libraries; 363 - } else { 364 - id.moduleNames = []; 365 - } 361 + const match = id.description.match(librariesRegex); 362 + if (match) { 363 + // eslint-disable-next-line prefer-destructuring 364 + const librariesRaw = match[1]; 365 + const libraries = librariesRaw.replace(/\\`/gu, "").split(/,\s*/gu); 366 + id.moduleNames = libraries; 367 + } else { 368 + id.moduleNames = []; 369 + } 366 370 } 367 371 368 372 /** ··· 372 376 */ 373 377 // eslint-disable-next-line max-lines-per-function 374 378 function parseChildNode(c: ChildNode, text: string[]) { 375 - switch (c.nodeName) { 376 - case "BR": 377 - text.push(`<br>\n`); 378 - break; 379 - case "B": 380 - text.push(`**${c.textContent?.replace(/\n/gu, " ").trim()}** `); 381 - break; 382 - case "TT": 383 - c.childNodes.forEach((cN) => { 384 - switch (cN.nodeName) { 385 - case "BR": 379 + switch (c.nodeName) { 380 + case "BR": 386 381 text.push(`<br>\n`); 387 382 break; 388 - case "B": 389 - text.push(`**${cN.textContent?.replace(/\n/gu, " ").trim()}** `); 383 + case "B": 384 + text.push(`**${c.textContent?.replace(/\n/gu, " ").trim()}** `); 390 385 break; 391 - case "I": 392 - text.push( 393 - "*`" + `${cN.textContent?.replace(/\n/gu, " ").trim()}` + "`*" 394 - ); 386 + case "TT": 387 + c.childNodes.forEach((cN) => { 388 + switch (cN.nodeName) { 389 + case "BR": 390 + text.push(`<br>\n`); 391 + break; 392 + case "B": 393 + text.push( 394 + `**${cN.textContent 395 + ?.replace(/\n/gu, " ") 396 + .trim()}** ` 397 + ); 398 + break; 399 + case "I": 400 + text.push( 401 + "*`" + 402 + `${cN.textContent 403 + ?.replace(/\n/gu, " ") 404 + .trim()}` + 405 + "`*" 406 + ); 407 + break; 408 + case "IMG": 409 + if ((cN as HTMLImageElement).src.endsWith("0.gif")) { 410 + text.push("=>"); 411 + } 412 + break; 413 + case "#text": 414 + text.push( 415 + // eslint-disable-next-line no-useless-concat 416 + "`" + 417 + `${cN.textContent?.replace(/\n/gu, " ")}` + 418 + "`" 419 + ); 420 + 421 + break; 422 + } 423 + }); 395 424 break; 396 - case "IMG": 397 - if ((cN as HTMLImageElement).src.endsWith("0.gif")) { 398 - text.push("=>"); 399 - } 425 + case "#text": 426 + text.push(`${c.textContent?.replace(/\n/gu, " ")}`); 400 427 break; 401 - case "#text": 402 - text.push( 403 - // eslint-disable-next-line no-useless-concat 404 - "`" + `${cN.textContent?.replace(/\n/gu, " ")}` + "`" 405 - ); 406 - 428 + case "SPAN": 429 + c.childNodes.forEach((cN) => parseChildNode(cN, text)); 407 430 break; 408 - } 409 - }); 410 - break; 411 - case "#text": 412 - text.push(`${c.textContent?.replace(/\n/gu, " ")}`); 413 - break; 414 - case "SPAN": 415 - c.childNodes.forEach((cN) => parseChildNode(cN, text)); 416 - break; 417 - } 431 + } 418 432 } 419 433 420 434 /** ··· 426 440 * @returns The sanitized description. 427 441 */ 428 442 function sanitizeDescription(text: string): string { 429 - let sanitized = text 430 - .replace(/[ ]+/gu, " ") 431 - .replace(/^ /gmu, "") 432 - .replace(/[ ]+\n/gu, "\n") 433 - .replace(/\n[\n]+$/u, "\n") 434 - .replace(/\n\n[\n]+/gu, "\n\n") 435 - // Non-breaking-space. 436 - .replace(/\u00A0/gu, " ") 437 - .replace(/\\/gu, "\\\\") 438 - .replace(/`/gu, "\\`"); 439 - const match = sanitized.match(exampleRegex); 440 - if (match) { 441 - // eslint-disable-next-line prefer-destructuring 442 - const example = match[1]; 443 - const exampleNoBackticks = example 444 - .replace(lineFormatRegex, "$1") 445 - .replace(/\\`/gu, "") 446 - .replace(/^ /gmu, ""); 447 - sanitized = sanitized.replace( 448 - example, 449 - "**Examples:**\n\n\\`\\`\\`scheme\n" + 450 - exampleNoBackticks + 451 - "\n\\`\\`\\`\n" 452 - ); 453 - } 454 - return sanitized; 443 + let sanitized = text 444 + .replace(/[ ]+/gu, " ") 445 + .replace(/^ /gmu, "") 446 + .replace(/[ ]+\n/gu, "\n") 447 + .replace(/\n[\n]+$/u, "\n") 448 + .replace(/\n\n[\n]+/gu, "\n\n") 449 + // Non-breaking-space. 450 + .replace(/\u00A0/gu, " ") 451 + .replace(/\\/gu, "\\\\") 452 + .replace(/`/gu, "\\`"); 453 + const match = sanitized.match(exampleRegex); 454 + if (match) { 455 + // eslint-disable-next-line prefer-destructuring 456 + const example = match[1]; 457 + const exampleNoBackticks = example 458 + .replace(lineFormatRegex, "$1") 459 + .replace(/\\`/gu, "") 460 + .replace(/^ /gmu, ""); 461 + sanitized = sanitized.replace( 462 + example, 463 + "**Examples:**\n\n\\`\\`\\`scheme\n" + 464 + exampleNoBackticks + 465 + "\n\\`\\`\\`\n" 466 + ); 467 + } 468 + return sanitized; 455 469 } 456 470 457 471 /** ··· 462 476 * `""` else. 463 477 */ 464 478 function stringOrEmpty(s: string | undefined | null): string { 465 - return s ? s.replace(/\u00A0/gu, " ") : ""; 479 + return s ? s.replace(/\u00A0/gu, " ") : ""; 466 480 } 467 481 468 482 /** ··· 471 485 * @returns The filename to use for the downloaded file from the given URL. 472 486 */ 473 487 function fileNameFromURL(url: URL) { 474 - return url.hostname + basename(url.pathname); 488 + return url.hostname + basename(url.pathname); 475 489 } 476 490 477 491 /** ··· 482 496 * @returns The content of the downloaded file. 483 497 */ 484 498 async function downloadAndRead(url: string) { 485 - const urlUrl = new URL(url); 486 - const downloadTo = fileNameFromURL(urlUrl); 487 - if (!existsSync(downloadTo)) { 488 - try { 489 - await download(urlUrl, downloadTo); 490 - } catch (exp) { 491 - console.error(`Caught "${exp}" trying to download from ${url}`); 492 - process.exit(1); 499 + const urlUrl = new URL(url); 500 + const downloadTo = fileNameFromURL(urlUrl); 501 + if (!existsSync(downloadTo)) { 502 + try { 503 + await download(urlUrl, downloadTo); 504 + } catch (exp) { 505 + console.error(`Caught "${exp}" trying to download from ${url}`); 506 + process.exit(1); 507 + } 493 508 } 494 - } 495 - filesToDelete.push(downloadTo); 496 - return new Promise<string>((resolve, reject) => { 497 - readFile(downloadTo, { encoding: "utf8" }, (r, d) => { 498 - if (r) { 499 - reject(r); 500 - } 501 - resolve(d); 509 + filesToDelete.push(downloadTo); 510 + return new Promise<string>((resolve, reject) => { 511 + readFile(downloadTo, { encoding: "utf8" }, (r, d) => { 512 + if (r) { 513 + reject(r); 514 + } 515 + resolve(d); 516 + }); 502 517 }); 503 - }); 504 518 } 505 519 506 520 /** ··· 510 524 * @returns Nothing. 511 525 */ 512 526 async function download(url: URL, fileName: string): Promise<void> { 513 - const fileStream = createWriteStream(fileName); 514 - return new Promise<void>((resolve, reject) => { 515 - https.get(url, (res) => { 516 - res.pipe(fileStream); 517 - res.on("error", (e) => reject(e)); 518 - fileStream.on("finish", () => 519 - fileStream.close((err) => { 520 - if (err) { 521 - reject(err); 522 - } 523 - resolve(); 524 - }) 525 - ); 527 + const fileStream = createWriteStream(fileName); 528 + return new Promise<void>((resolve, reject) => { 529 + https.get(url, (res) => { 530 + res.pipe(fileStream); 531 + res.on("error", (e) => reject(e)); 532 + fileStream.on("finish", () => 533 + fileStream.close((err) => { 534 + if (err) { 535 + reject(err); 536 + } 537 + resolve(); 538 + }) 539 + ); 540 + }); 526 541 }); 527 - }); 528 542 } 529 543 530 544 /** ··· 532 546 * @param text The text to save. 533 547 */ 534 548 async function writeFunctionDocumentation(text: string): Promise<void> { 535 - return new Promise<void>((resolve, reject) => { 536 - writeFile(outFilename, text, { encoding: "utf8" }, (r) => { 537 - if (r) { 538 - reject(r); 539 - } 540 - resolve(); 549 + return new Promise<void>((resolve, reject) => { 550 + writeFile(outFilename, text, { encoding: "utf8" }, (r) => { 551 + if (r) { 552 + reject(r); 553 + } 554 + resolve(); 555 + }); 541 556 }); 542 - }); 543 557 } 544 558 545 559 main();