1. Scene
used to convert the current package Compare the package in JSON with the package management tool library (such as npm). If there is a package that can be updated, you will be prompted.
2. Use
1) Installation
$ npm install update-notifier
2) Use
const updateNotifier = require('update-notifier'); const pkg = require('./package.json'); const notifier = updateNotifier({pkg}); notifier.notify(); console.log(notifier.update); /* { latest: '1.0.1', current: '1.0.0', type: 'patch', // Possible values: latest, major, minor, patch, prerelease, build name: 'pageres' } */
3. Source code
1) Read readme md
Source address: https://github.com/yeoman/update-notifier
You can learn the following from readme
-
You can run example. In the root directory JS for debugging
-
For the sake of user experience, update notifier does not check every time it runs, but after a certain time interval
-
The update notifier check will start a child process, even though process. Is called Exit exits the process and does not affect the check
2) Clone source repository
# clone git clone https://github.com/yeoman/update-notifier.git # Installation dependency cd update-notifier npm install
3) Operation debugging
1) Via package JSON, you can see that the debugging command is as follows
npm run test
2) View debugging results
the information will not be printed during the first run, and the update information will be prompted only after the second run.
this is because the old version cannot be found for comparison during the first startup. Therefore, the first information will be stored persistently, and the last stored information will be compared with the results of this operation during the second execution.
4) Source code interpretation
example.js
'use strict'; const updateNotifier = require('.'); // Pass a package information into // The first run will the package 0.9 Version 2 stored // The second run will get 0.9 2 comparison with the latest version in the existing library updateNotifier({ pkg: { name: 'public-ip', version: '0.9.2' }, // Check interval updateCheckInterval: 0 }).notify();
check.js
/* eslint-disable unicorn/no-process-exit */ 'use strict'; let updateNotifier = require('.'); const options = JSON.parse(process.argv[2]); updateNotifier = new updateNotifier.UpdateNotifier(options); (async () => { // If the run times out, exit the process setTimeout(process.exit, 1000 * 30); // Get the package version information obtained from the update check const update = await updateNotifier.fetchInfo(); // Take the current time as the last check update time updateNotifier.config.set('lastUpdateCheck', Date.now()); // If the current package is not the latest version, a prompt is thrown if (update.type && update.type !== 'latest') { updateNotifier.config.set('update', update); } process.exit(); })().catch(error => { console.error(error); process.exit(1); });
index.js
Note: index There are many JS source codes. The following will be divided into multiple code blocks for interpretation
The overall code flow is understood as:
- Run for the first time and extract the current package The version information of all packages stored in JSON, generate files and store them persistently, and record the current time
- In the next run, compare the current time with the time of the last generated file. If the check interval is exceeded, perform udpate check. Check and compare the npm library. If you find that the package version can be updated, you will be prompted on the console
Follow the process to read the source code
1) Introduction Kit
'use strict'; const {spawn} = require('child_process'); const path = require('path'); const {format} = require('util'); // Lazy loading module: it can be imported only when the corresponding package is called const importLazy = require('import-lazy')(require); const configstore = importLazy('configstore'); const chalk = importLazy('chalk'); const semver = importLazy('semver'); const semverDiff = importLazy('semver-diff'); const latestVersion = importLazy('latest-version'); const isNpm = importLazy('is-npm'); const isInstalledGlobally = importLazy('is-installed-globally'); const isYarnGlobal = importLazy('is-yarn-global'); const hasYarn = importLazy('has-yarn'); const boxen = importLazy('boxen'); const xdgBasedir = importLazy('xdg-basedir'); const isCi = importLazy('is-ci'); const pupa = importLazy('pupa'); const ONE_DAY = 1000 * 60 * 60 * 24;
2) The UpdateNotifier class is declared. There is no special description later. The scope is in the class
class UpdateNotifier { ... }
3) Declare the constructor of the class
class UpdateNotifier { constructor(options = {}) { this.options = options; options.pkg = options.pkg || {}; options.distTag = options.distTag || 'latest'; // Read package name and version number information options.pkg = { name: options.pkg.name || options.packageName, version: options.pkg.version || options.packageVersion }; if (!options.pkg.name || !options.pkg.version) { throw new Error('pkg.name and pkg.version required'); } this.packageName = options.pkg.name; this.packageVersion = options.pkg.version; // Update check interval, default: 1 day this.updateCheckInterval = typeof options.updateCheckInterval === 'number' ? options.updateCheckInterval : ONE_DAY; this.disabled = 'NO_UPDATE_NOTIFIER' in process.env || process.env.NODE_ENV === 'test' || // process.argv: Node. Command line parameters passed in during JS process process.argv.includes('--no-update-notifier') || // CI continuous integration environment isCi(); this.shouldNotifyInNpmScript = options.shouldNotifyInNpmScript; if (!this.disabled) { // Generate file storage version information try { const ConfigStore = configstore(); this.config = new ConfigStore(`update-notifier-${this.packageName}`, { optOut: false, // When generating a file, record the current time to facilitate the next comparison of whether it exceeds the inspection interval lastUpdateCheck: Date.now() }); } catch { const message = chalk().yellow(format(' %s update check failed ', options.pkg.name)) + format('\n Try running with %s or get access ', chalk().cyan('sudo')) + '\n to the local update config store via \n' + chalk().cyan(format(' sudo chown -R $USER:$(id -gn $USER) %s ', xdgBasedir().config)); process.on('exit', () => { console.error(boxen()(message, {align: 'center'})); }); } } } ... }
4) Declare check method
class UpdateNotifier { ... check() { if ( !this.config || this.config.get('optOut') || this.disabled ) { return; } // Read the version information obtained from this check this.update = this.config.get('update'); if (this.update) { // Use the latest version instead of the cached version this.update.current = this.packageVersion; // Clear information from cache this.config.delete('update'); } // Only check for updates on a set interval // Check only within the inspection interval, for example, set no repeat inspection within 1 week if (Date.now() - this.config.get('lastUpdateCheck') < this.updateCheckInterval) { return; } // Use child processes to perform tasks spawn(process.execPath, [path.join(__dirname, 'check.js'), JSON.stringify(this.options)], { detached: true, stdio: 'ignore' }).unref(); } }
5) Get version information
class UpdateNotifier { ... // Get version information async fetchInfo() { const {distTag} = this.options; const latest = await latestVersion()(this.packageName, {version: distTag}); return { latest, current: this.packageVersion, // Check whether the package is the latest version and contact check JS type === 'latest' type: semverDiff()(this.packageVersion, latest) || distTag, name: this.packageName }; } }
6) Send notification reminder
class UpdateNotifier { ... notify(options) { const suppressForNpm = !this.shouldNotifyInNpmScript && isNpm().isNpmOrYarn; if (!process.stdout.isTTY || suppressForNpm || !this.update || !semver().gt(this.update.latest, this.update.current)) { return this; } options = { isGlobal: isInstalledGlobally(), isYarnGlobal: isYarnGlobal()(), ...options }; // Message reminder template let installCommand; if (options.isYarnGlobal) { installCommand = `yarn global add ${this.packageName}`; } else if (options.isGlobal) { installCommand = `npm i -g ${this.packageName}`; } else if (hasYarn()()) { installCommand = `yarn add ${this.packageName}`; } else { installCommand = `npm i ${this.packageName}`; } // Message reminder template const defaultTemplate = 'Update available ' + chalk().dim('{currentVersion}') + chalk().reset(' → ') + chalk().green('{latestVersion}') + ' \nRun ' + chalk().cyan('{updateCommand}') + ' to update'; const template = options.message || defaultTemplate; // Message reminder border style options.boxenOptions = options.boxenOptions || { padding: 1, margin: 1, align: 'center', borderColor: 'yellow', borderStyle: 'round' }; // Splicing reminder message const message = boxen()( pupa()(template, { packageName: this.packageName, currentVersion: this.update.current, latestVersion: this.update.latest, updateCommand: installCommand }), options.boxenOptions ); if (options.defer === false) { console.error(message); } else { process.on('exit', () => { console.error(message); }); process.on('SIGINT', () => { console.error(''); process.exit(); }); } return this; } }