| 413 | } |
| 414 | |
| 415 | #writeLog (level, meta, ...args) { |
| 416 | const levelOpts = LEVEL_METHODS[level] |
| 417 | const show = levelOpts.show ?? (({ index }) => levelOpts.index <= index) |
| 418 | const force = meta.force && !this.#silent |
| 419 | |
| 420 | if (force || show({ index: this.#levelIndex, timing: this.#timing })) { |
| 421 | // this mutates the array so we can pass args directly to format later |
| 422 | const title = args.shift() |
| 423 | const prefix = [ |
| 424 | this.#logColors.heading(this.#heading), |
| 425 | this.#logColors[level](level), |
| 426 | title ? this.#logColors.title(title) : null, |
| 427 | ] |
| 428 | const writeOpts = { prefix } |
| 429 | // notice logs typically come from `npm-notice` headers in responses. Some of them have 2fa login links so we skip redaction. |
| 430 | if (level === 'notice') { |
| 431 | writeOpts.redact = false |
| 432 | // Deduplicate notices within a single command execution, unless in verbose mode |
| 433 | if (this.#levelIndex < LEVEL_OPTIONS.verbose.index) { |
| 434 | const noticeKey = JSON.stringify([title, ...args]) |
| 435 | if (this.#seenNotices.has(noticeKey)) { |
| 436 | return |
| 437 | } |
| 438 | this.#seenNotices.add(noticeKey) |
| 439 | } |
| 440 | } |
| 441 | this.#write(this.#stderr, writeOpts, ...args) |
| 442 | } |
| 443 | } |
| 444 | } |
| 445 | |
| 446 | class Progress { |