Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
update-notifier.js116 linesDownload Raw Back to cli
1// print a banner telling the user to upgrade npm to latest but not in CI, and not if we're doing that already.
2
3const ciInfo = require('ci-info')
4const gt = require('semver/functions/gt')
5const gte = require('semver/functions/gte')
6const parse = require('semver/functions/parse')
7const { stat, writeFile } = require('node:fs/promises')
8const { resolve } = require('node:path')
9
10// update check frequency
11const DAILY = 1000 * 60 * 60 * 24
12const WEEKLY = DAILY * 7
13
14// don't put it in the _cacache folder, just in npm's cache
15const lastCheckedFile = npm => resolve(npm.flatOptions.cache, '../_update-notifier-last-checked')
16
17// Actual check for updates. This is a separate function so that we only load this if we are doing the actual update
18const updateCheck = async (npm, spec, version, current) => {
19  const pacote = require('pacote')
20
21  const mani = await pacote.manifest(`npm@${spec}`, {
22    // always prefer latest, even if doing --tag=whatever on the cmd
23    defaultTag: 'latest',
24    ...npm.flatOptions,
25    cache: false,
26  }).catch(() => null)
27
28  // if pacote failed, give up
29  if (!mani) {
30    return null
31  }
32
33  const latest = mani.version
34
35  // if the current version is *greater* than latest, we're on a 'next' and should get the updates from that release train.
36  // Note that this isn't another http request over the network, because the packument will be cached by pacote from previous request.
37  if (gt(version, latest) && spec === '*') {
38    return updateNotifier(npm, `^${version}`)
39  }
40
41  // if we already have something >= the desired spec, then we're done
42  if (gte(version, latest)) {
43    return null
44  }
45
46  const chalk = npm.logChalk
47
48  // ok! notify the user about this update they should get.
49  // The message is saved for printing at process exit so it will not get lost in any other messages being printed as part of the command.
50  const update = parse(mani.version)
51  const type = update.major !== current.major ? 'major'
52    : update.minor !== current.minor ? 'minor'
53    : update.patch !== current.patch ? 'patch'
54    : 'prerelease'
55  const typec = type === 'major' ? 'red'
56    : type === 'minor' ? 'yellow'
57    : 'cyan'
58  const message = [
59    '',
60    `New ${chalk[typec](type)} version of npm available! ${chalk[typec](current)} -> ${chalk.blue(latest)}`,
61    `Changelog: ${chalk.blue(`https://github.com/npm/cli/releases/tag/v${latest}`)}`,
62    `To update run: ${chalk.underline(`npm install -g npm@${latest}`)}`,
63    '',
64  ].join('\n')
65
66  return message
67}
68
69const updateNotifier = async (npm, spec = '*') => {
70  // if we're on a prerelease train, then updates are coming fast check for a new one daily.
71  // otherwise, weekly.
72  const { version } = npm
73  const current = parse(version)
74
75  // if we're on a beta train, always get the next beta
76  if (current.prerelease.length) {
77    spec = `^${version}`
78  }
79
80  // while on a beta train, get updates daily
81  const duration = current.prerelease.length ? DAILY : WEEKLY
82
83  const t = new Date(Date.now() - duration)
84  // if we don't have a file, then definitely check it.
85  const st = await stat(lastCheckedFile(npm)).catch(() => ({ mtime: t - 1 }))
86
87  // if we've already checked within the specified duration, don't check again
88  if (!(t > st.mtime)) {
89    return null
90  }
91
92  // intentional.  do not await this.  it's a best-effort update.
93  // if this fails, it's ok.
94  // might be using /dev/null as the cache or something weird like that.
95  writeFile(lastCheckedFile(npm), '').catch(() => {})
96
97  return updateCheck(npm, spec, version, current)
98}
99
100module.exports = async npm => {
101  if (
102    // opted out
103    !npm.config.get('update-notifier')
104    // global npm update
105    || (npm.flatOptions.global &&
106      ['install', 'update'].includes(npm.command) &&
107      npm.argv.some(arg => /^npm(@|$)/.test(arg)))
108    // CI
109    || ciInfo.isCI
110  ) {
111    return null
112  }
113
114  return updateNotifier(npm)
115}
116 
codekingpro/portable-devtools · Team Ai