Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[epic] npm logging, output, and cleaning #810

Closed
20 of 22 tasks
wraithgar opened this issue Mar 26, 2024 · 4 comments
Closed
20 of 22 tasks

[epic] npm logging, output, and cleaning #810

wraithgar opened this issue Mar 26, 2024 · 4 comments
Assignees

Comments

@wraithgar
Copy link
Member

wraithgar commented Mar 26, 2024

Epic

We are going to consolidate and clean up the code npm uses for output, logging, and for sanitizing strings before doing those things.

Currently these are the modules used:

This is likely not a comprehensive list, and not everything here belongs in our new consolidated package for npm output concerns.

Tasks

  1. Bug Priority 1 Release 10.x
    lukekarrys

Notes

npm progress bars will go away for now

This replaces the following issues

@lukekarrys
Copy link
Contributor

Putting some notes here for future improvements that this will now unlock:

npm.chalk/logChalk/color/logColor

Remove npm getters for chalk, logChalk, color and logColor. These are used throughout npm/cli so we have can call chalk.red(someText) and have it respect the --color config including the inferred value based on the stdout stream. This was introduced to take the place of color/logColor booleans but those are still in use some places.

  • They are also used in other dependencies (non-exhaustive list)
    • libnpmexec
    • npm-audit-report
  • In order for dependencies to emit colorized output we would need a solution like chalk-template which would allow for the emitted data to contain the colorized that should be used if colors are allowed, and to have them stripped out if not.
    • chalk-template is one solution to this. Another would be something like (first draft untested code follows). The only important bit is that producers can send signal data hinting at what colors to use without needing a parent to pass any configuration in.
// producer
require('proc-log').output.standard({
  // Note this is not a good API because we pass everything through util.format
  // and this solution relies on a string property like `withChalk` being well-known
  // and not used anywhere else. We could symbols instead but they'd need to be exported
  withChalk: (c) => `${c.red('this is red') ${c.green('this is green'}`
})

// consumer
process.on('output', (level, value) => {
  if (level === 'standard' && typeof value.withChalk === 'function') {
    process.stdout.write(value.withChalk(myStdoutConfigChalkInstance))
  } 
})

read()

Previously anywhere we called read() (or anything that requested user input like readline()) we needed to manually call methods like npmlog.(endableProgress|disableProgress)() or more recently proc-log.(pause|resume)().

Now that we have all display functionality in a single place, we can make requesting user input also be events based. The only difference is that those events need to be await-ed.

Here's an example of what could happen now:

// add this to proc-log
module.exports = {
  input: {
    read: function (...args) {
      let resolve, reject
      const promise = new Promise((_res, _rej) => {
        resolve = _res
        reject = _rej
      })
      process.emit('input, 'read', resolve, reject, ...args)
      return promise
    }
  }
}

// producer
const { log, input, output } = require('proc-log')

log.warn('Some warning')
output.standard('About to ask for some input')
const res = await input.read({ prompt: 'favorite color?' })
output.standard(`You said ${res}`)

// consumer
const { read } = require('read')

process.on('input', (level, ...args) => {
  if (level === 'read') {
    // Should hide progress, logs, etc
    const [resolve, reject, opts] = args
    read(opts).then(resolve).catch(reject).finally(() => {
      // Should restore progress, logs, etc
    })
  }
})

@lukekarrys
Copy link
Contributor

Customizable colors

npmlog used to be in charge of colors which we set once at runtime. The Display class now generate a color palette on load which takes configured options.

Our goal is to have one (1) good and accessible color palette for all output shown to the terminal. BUT if we find cases where that is impossible (eg dark vs light terminal backgrounds) it is now at least possible to have a config option to change colors.

@lukekarrys
Copy link
Contributor

lukekarrys commented Apr 13, 2024

Command templates

What if a command could do this?

// producer
const data = { name, version }
output.standard(data, {
  templates: {
    // pick any templating DSL
    human: `{{ name }}@{{ version }}`,
    parseable: `{{ name }}\t{{ version }}`
  }
})

// consumer
process.on('output', (level, data, { templates }) => {
  if (level === 'standard') {
    const templateName = templates[this.human ? 'human' : this.parseable : 'parseable' : '']
    process.stdout.write(template(data, templateName || JSON.stringify))
  }
})

@wraithgar
Copy link
Member Author

Closing this out. This epic is considered done. There are follow up tasks that we will put back into the planning queue. This will all go out w/ the next npm release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants