({useStale = false}: LoadOptions = {}, effects = defaultEffects)
| 399 | } |
| 400 | |
| 401 | async load({useStale = false}: LoadOptions = {}, effects = defaultEffects): Promise<string> { |
| 402 | const loaderPath = join(this.root, this.path); |
| 403 | const key = join(this.root, this.targetPath); |
| 404 | let command = runningCommands.get(key); |
| 405 | if (!command) { |
| 406 | command = (async () => { |
| 407 | const outputPath = join(".observablehq", "cache", this.targetPath); |
| 408 | const cachePath = join(this.root, outputPath); |
| 409 | const loaderStat = await maybeStat(loaderPath); |
| 410 | const cacheStat = await maybeStat(cachePath); |
| 411 | if (!cacheStat) effects.output.write(faint("[missing] ")); |
| 412 | else if (cacheStat.mtimeMs < loaderStat!.mtimeMs) { |
| 413 | if (useStale) return effects.output.write(faint("[using stale] ")), outputPath; |
| 414 | else effects.output.write(faint("[stale] ")); |
| 415 | } else return effects.output.write(faint("[fresh] ")), outputPath; |
| 416 | const tempPath = join(this.root, ".observablehq", "cache", `${this.targetPath}.${process.pid}`); |
| 417 | const errorPath = tempPath + ".err"; |
| 418 | const errorStat = await maybeStat(errorPath); |
| 419 | if (errorStat) { |
| 420 | if (errorStat.mtimeMs > loaderStat!.mtimeMs && errorStat.mtimeMs > -1000 + Date.now()) |
| 421 | throw new Error("loader skipped due to recent error"); |
| 422 | else await unlink(errorPath).catch(() => {}); |
| 423 | } |
| 424 | await prepareOutput(tempPath); |
| 425 | await prepareOutput(cachePath); |
| 426 | const tempFd = await open(tempPath, "w"); |
| 427 | try { |
| 428 | await this.exec(tempFd.createWriteStream({highWaterMark: 1024 * 1024}), {useStale}, effects); |
| 429 | await rename(tempPath, cachePath); |
| 430 | } catch (error) { |
| 431 | await rename(tempPath, errorPath); |
| 432 | throw error; |
| 433 | } finally { |
| 434 | await tempFd.close(); |
| 435 | } |
| 436 | return outputPath; |
| 437 | })(); |
| 438 | command.finally(() => runningCommands.delete(key)).catch(() => {}); |
| 439 | runningCommands.set(key, command); |
| 440 | } |
| 441 | effects.output.write(`${cyan("load")} ${this.targetPath} ${faint("→")} `); |
| 442 | const start = performance.now(); |
| 443 | command.then( |
| 444 | (path) => { |
| 445 | const {size} = statSync(join(this.root, path)); |
| 446 | effects.logger.log( |
| 447 | `${green("success")} ${size ? cyan(formatByteSize(size)) : yellow("empty output")} ${faint( |
| 448 | `in ${formatElapsed(start)}` |
| 449 | )}` |
| 450 | ); |
| 451 | }, |
| 452 | (error) => { |
| 453 | effects.logger.log(`${red("error")} ${faint(`in ${formatElapsed(start)}:`)} ${red(error.message)}`); |
| 454 | } |
| 455 | ); |
| 456 | return command; |
| 457 | } |
| 458 |
nothing calls this directly
no test coverage detected