当前位置: 代码迷 >> 综合 >> 【源码】update-notifier 检测 npm 包是否更新~
  详细解决方案

【源码】update-notifier 检测 npm 包是否更新~

热度:91   发布时间:2023-12-10 14:02:13.0
学习目标
  • 了解update-notifier作用和使用场景
  • 应用场景:
    检测npm包是否更新,比如组件库更新或者其他npm 包更新,
资料准备

链接:https://github.com/yeoman/update-notifier

什么是update-notifier

提示包需要更新

本质就是开了child_process运行在后台,就是通过比较 package.json 中的 nameversion 和当前 npm registry 上的最新版,如果检查到有可用更新版,会将结果保存在.update属性中,是用来标记是否有可用更新版,如果有,就输出更新信息,存在可用更新版

前期准备
git clone https://github.com/yeoman/update-notifier
cd update-notifier
npm i
初识–官方文档
  1. 用法
  • simple
    const updateNotifier = require('update-notifier');const pkg = require('./package.json');updateNotifier({pkg}).notify();
  • Comprehensive
	const updateNotifier = require('update-notifier');const pkg = require('./package.json');// Checks for available update and returns an instanceconst notifier = updateNotifier({pkg});// Notify using the built-in convenience methodnotifier.notify();// `notifier.update` contains some useful info about the updateconsole.log(notifier.update);/*{latest: '1.0.1',current: '1.0.0',type: 'patch', // Possible values: latest, major, minor, patch, prerelease, buildname: 'pageres'}*/
  • 选项和自定义消息
    const notifier = updateNotifier({pkg,updateCheckInterval: 1000 * 60 * 60 * 24 * 7 // 1 week
    });if (notifier.update) {console.log(`Update available: ${notifier.update.latest}`);
    }
    
  1. HOW

    用户第一次运行应用程序时,将检查是否有更新,即使有可用的更新,也会等待指定的updateCheckInterval,然后再通知用户。这样做是为了不打扰用户。

  2. API

    • notifier = updateNotifier(options)
      检查是否有可用的更新。接受下面定义的选项。如果存在可用更新,则返回具有.update属性的实例,否则返回未定义的实例。
          options: {pkg: {}, // 对象类型name: '', //必须的,字符串类型version: '', //必须的,字符串类型updateCheckInterval:'',//number类型,默认值为一天 (1000 * 60 * 60 * 24)shouldNotifyInNpmScript: false,//布尔值,默认为false, 是否允许在作为npm脚本运行时显示通知distTag:'latest'//字符串,默认值为latest}
      
    • notifier.fetchInfo()
      检查更新信息,返回具有以下内容的对象
      {latest: '' ,//字符串,最新版本current:'',//字符串,当前版本type:'',//字符串,当前更新的类型,可能取值为:latest, major, minor, patch, prerelease, buildname:'' // 字符串,包名}
      
    • notifier.notify(options?)
      显示通知消息的便捷方法,仅在有更新且进程为TTY时通知。options:
          {defer: true, // 布尔值,默认为true, 在进程退出后将通知延迟显示message: '', // string,更新可用时将显示的消息isGlobal:true/false, // 布尔值,更新可用时将显示的消息在默认消息的npm i建议中包括-g参数。boxenOptions:object,// 一个对象,传给boxen的参数}
      
解读index.js

引用包部分

  1. const {spawn} = require('child_process');
  • node.js中 杀死正在执行的子进程
  • const {spawn} = require('child_process');const spawn = require('child_process');的区别:
    1. {x} = v相当于{x:x} = v
    2. {x:y} = v相当于 y = v.x
    3. const {spawn} = require('child_process');相当于 const_ = require('child_process'); const spawn = _.resolve;
    4. 相当于解构出来的东西,在这个当前的文件里面有了这个东西,已经获取到了,可以直接使用,而不需要在通过spawn.xxxx 去拿相应的变量或者方法了
  1. const path = require('path');

node.js中 处理关于文件路径的问题链接

  1. const {format} = require('util');
  • util是常用的函数集合,用于弥补核心js功能过于精简的不足,为了满足nide.js 内部api 的需求
  • util.format(format[, …])
    1. util.form()函数接收 一个格式化字符串format作为第一个参数,并返回格式化后的字符串。
    2. format参数是可用包含零个或多个占位符的字符串,每个占位符都是以一个%字符开始,并最终被对应的参数转换的字符串值取代
  1. const importLazy = require('import-lazy')(require); usage
const importLazy = require('import-lazy')(require);//模块懒加载
const configstore = importLazy('configstore');// 轻松加载和持久化配置,而无需考虑在何处以及如何加载。
const chalk = importLazy('chalk'); //画彩色字体通知的
const semver = importLazy('semver'); //npm的语义版本器
const semverDiff = importLazy('semver-diff');// 获取两个semver版本的差异类型 //例如:0.0.1 0.0.2→ patch
const latestVersion = importLazy('latest-version');// 获取npm包的最新版本
const isNpm = importLazy('is-npm');//检查代码是不是作为npm 脚本运行
const isInstalledGlobally = importLazy('is-installed-globally'); //检查软件包是否是通过 npm 全局安装的
const isYarnGlobal = importLazy('is-yarn-global'); // 检查是否在没有任何fs调用的情况下由yarn全局安装
const hasYarn = importLazy('has-yarn'); //检查项目是否正在使用yarn
const boxen = importLazy('boxen');//在终端创建框
const xdgBasedir = importLazy('xdg-basedir');//获取XDG基本目录路径
const isCi = importLazy('is-ci');//如果当前环境是持续集成服务器,则返回true
const pupa = importLazy('pupa');//简易微模版const ONE_DAY = 1000 * 60 * 60 * 24; //24小时60分钟60秒1000毫秒
UpdateNotifier
class UpdateNotifier {constructor(options = {}) { }check(){}async fetchInfo() {}notify(options) {}
}
consructor初始化阶段
class UpdateNotifier {constructor(options = {}) {// 把package.json的东西给optionsthis.options = options;// 容错处理options.pkg = options.pkg || {};options.distTag = options.distTag || 'latest';// Reduce pkg to the essential keys. with fallback to deprecated options// TODO: Remove deprecated options at some point far into the futureoptions.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;/*** 通过读官方文档可以知道 updateCheckInterval 默认是一天(默认是数字格式) 此处检查周期,单位ms* const ONE_DAY = 1000 * 60 * 60 * 24; //24小时60分种60秒1000毫秒*/this.updateCheckInterval = typeof options.updateCheckInterval === 'number' ? options.updateCheckInterval : ONE_DAY;/*** isCi()  如果当前环境是持续集成服务器,则返回true* 禁用状态检测:是否禁止更新通知*/this.disabled = 'NO_UPDATE_NOTIFIER' in process.env ||process.env.NODE_ENV === 'test' ||process.argv.includes('--no-update-notifier') ||isCi();/*** shouldNotifyInNpmScript 是否允许在作为npm脚本运行时显示通知*/this.shouldNotifyInNpmScript = options.shouldNotifyInNpmScript;if (!this.disabled) {// 如果没有禁止更新通知try {//设置配置对象const ConfigStore = configstore();// 使用configstore获取本地该包相关信息的存储,第一次拿不到this.config = new ConfigStore(`update-notifier-${this.packageName}`, {optOut: false,// Init with the current time so the first check is only// after the set interval, so not to bother users right away// 设置当前时间为检查间隔的起点,不影响使用者  此时第一次启动时不通知的原因就在此处lastUpdateCheck: Date.now()});} catch {// Expecting error code EACCES or EPERM// 提示需错误信息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'}));});}}}check() {}async fetchInfo() {}notify(options) {}
}
check
	check() {/*** 如果获取不到本地该包相关信息的存储 || optOut 为 true || 禁止更新通知 的情况时停止检查*/if (!this.config || this.config.get('optOut') || this.disabled) {return;}//获取相关包的更新信息,第一次为undefinedthis.update = this.config.get('update');if (this.update) {// Use the real latest version instead of the cached one//如果获取到最新的包的信息了,使用实际值替代缓存值this.update.current = this.packageVersion;// Clear cached information 清除缓存的值this.config.delete('update');}// Only check for updates on a set interval//仅在设定的时间间隔内检查更新,如果还在周期内就返回,停止检查if (Date.now() - this.config.get('lastUpdateCheck') < this.updateCheckInterval) {return;}// Spawn a detached process, passing the options as an environment property/*** 生成分离的进程,将选项作为环境属性传递* path.join 将多个参数值合并成一个路径*/spawn(process.execPath, [path.join(__dirname, 'check.js'), JSON.stringify(this.options)], {//让子进程独立于其父进程运行子进程可以在父进程退出后继续运行,不管它们是否分离。[链接](http://nodejs.cn/api/child_process/options_detached.html)detached: true,// 不关注控制台的输入输出,忽略父进程的终止stdio: 'ignore'}).unref();// unref 方法用来断绝关系,这样“父”进程可以独立退出(不会导致子进程跟着退出)}
check.js
/* eslint-disable unicorn/no-process-exit */
'use strict';
// 获取index.js导出的内容
let updateNotifier = require('.');// 获取options
const options = JSON.parse(process.argv[2]);
//实例化 updateNotifier
updateNotifier = new updateNotifier.UpdateNotifier(options);//执行一个函数
(async () => {// Exit process when offline 脱机时退出进程setTimeout(process.exit, 1000 * 30);// 调用updateNotifier的fetchInfo方法获取更新信息const update = await updateNotifier.fetchInfo();// Only update the last update check time on success 仅在成功时更新上次更新的检查时间updateNotifier.config.set('lastUpdateCheck', Date.now());// 只要不是最新的包 就设置更新字段if (update.type && update.type !== 'latest') {updateNotifier.config.set('update', update);}// 获取到的信息,如果版本有更新,存到config// Call process exit explicitly to terminate the child process,// otherwise the child process will run forever, according to the Node.js docs// 显式调用进程退出以终止子进程,根据Node.js文档,否则子进程将永远运行process.exit();
})().catch(error => {console.error(error);process.exit(1);
});
fetchInfo 获取包的相关信息
	//获取包的相关信息async fetchInfo() {const {distTag} = this.options;// latestVersion 获取npm包的最新版本// 由于是懒加载的 所以需要先latestVersion() 执行下// 这里需要注意latestVersion是一个异步的请求const latest = await latestVersion()(this.packageName, {version: distTag});// 返回组装后的对象信息return {latest,current: this.packageVersion,type: semverDiff()(this.packageVersion, latest) || distTag,name: this.packageName};}notify(options) {}
notify 通知
	notify(options) {// 是否支持npm-scripts 调用const suppressForNpm = !this.shouldNotifyInNpmScript && isNpm().isNpmOrYarn;/*** !process.stdout.isTTY node不在终端上运行* suppressForNpm 不支持npm-scripts 调用* !this.update 本地没有检查过更新* !semver().gt(this.update.latest, this.update.current) 当前版本是否落后于最新版本* 满足以上条件时不需要通知(没有更新时不通知)*/if (!process.stdout.isTTY || suppressForNpm || !this.update || !semver().gt(this.update.latest, this.update.current)) {return this;}options = {// 检查软件包是否是通过 npm 全局安装的isGlobal: isInstalledGlobally(),// 是否是通过 yarn 全局安装的// 有个缺陷: 前提是用户没有改变yarn默认的全局安装目录isYarnGlobal: isYarnGlobal()(),...options};// 相应的更新命令提示let installCommand;if (options.isYarnGlobal) { // 是否是通过yarn全局安装installCommand = `yarn global add ${this.packageName}`;} else if (options.isGlobal) { //是否是通过npm 全局安装installCommand = `npm i -g ${this.packageName}`;} else if (hasYarn()()) {// hasYarn() 由于 hasYarn 是懒加载的模块,所以需要先执行一下 yarn 局部安装installCommand = `yarn add ${this.packageName}`;} else {// npm 局部安装installCommand = `npm i ${this.packageName}`;}const defaultTemplate = 'Update available ' +chalk().dim('{currentVersion}') +chalk().reset(' → ') +chalk().green('{latestVersion}') +' \nRun ' + chalk().cyan('{updateCommand}') + ' to update';//提示信息模板const template = options.message || defaultTemplate;options.boxenOptions = options.boxenOptions || {padding: 1,margin: 1,align: 'center',borderColor: 'yellow',borderStyle: 'round'};/*** boxen 在终端创建框* pupa 简易微模版*/const message = boxen()(pupa()(template, {// 将以下信息补全到模板中// this.update 是在 check 阶段填充的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的目的: 为了链式调用 option配置已改变return this;}
总结流程
  • const updateNotifier = new UpdateNotifier({})
    • 初始化 constuctor
    • updateNotifier.check()
  • 判断是否执行 check.js,如果执行:
    • updateNotifier.fetchInfo()
    • set(‘lastUpdateCheck’)
    • set(‘update’)
  • notify()
学习目标
  • 了解update-notifier作用和使用场景
  • 应用场景:
    检测npm包是否更新,比如组件库更新或者其他npm 包更新,
资料准备

链接:https://github.com/yeoman/update-notifier

什么是update-notifier

提示包需要更新

本质就是开了child_process运行在后台,就是通过比较 package.json 中的 nameversion 和当前 npm registry 上的最新版,如果检查到有可用更新版,会将结果保存在.update属性中,是用来标记是否有可用更新版,如果有,就输出更新信息,存在可用更新版

前期准备
git clone https://github.com/yeoman/update-notifier
cd update-notifier
npm i
初识–官方文档
  1. 用法
  • simple
    const updateNotifier = require('update-notifier');const pkg = require('./package.json');updateNotifier({pkg}).notify();
  • Comprehensive
	const updateNotifier = require('update-notifier');const pkg = require('./package.json');// Checks for available update and returns an instanceconst notifier = updateNotifier({pkg});// Notify using the built-in convenience methodnotifier.notify();// `notifier.update` contains some useful info about the updateconsole.log(notifier.update);/*{latest: '1.0.1',current: '1.0.0',type: 'patch', // Possible values: latest, major, minor, patch, prerelease, buildname: 'pageres'}*/
  • 选项和自定义消息
    const notifier = updateNotifier({pkg,updateCheckInterval: 1000 * 60 * 60 * 24 * 7 // 1 week
    });if (notifier.update) {console.log(`Update available: ${notifier.update.latest}`);
    }
    
  1. HOW

    用户第一次运行应用程序时,将检查是否有更新,即使有可用的更新,也会等待指定的updateCheckInterval,然后再通知用户。这样做是为了不打扰用户。

  2. API

    • notifier = updateNotifier(options)
      检查是否有可用的更新。接受下面定义的选项。如果存在可用更新,则返回具有.update属性的实例,否则返回未定义的实例。
          options: {pkg: {}, // 对象类型name: '', //必须的,字符串类型version: '', //必须的,字符串类型updateCheckInterval:'',//number类型,默认值为一天 (1000 * 60 * 60 * 24)shouldNotifyInNpmScript: false,//布尔值,默认为false, 是否允许在作为npm脚本运行时显示通知distTag:'latest'//字符串,默认值为latest}
      
    • notifier.fetchInfo()
      检查更新信息,返回具有以下内容的对象
      {latest: '' ,//字符串,最新版本current:'',//字符串,当前版本type:'',//字符串,当前更新的类型,可能取值为:latest, major, minor, patch, prerelease, buildname:'' // 字符串,包名}
      
    • notifier.notify(options?)
      显示通知消息的便捷方法,仅在有更新且进程为TTY时通知。options:
          {defer: true, // 布尔值,默认为true, 在进程退出后将通知延迟显示message: '', // string,更新可用时将显示的消息isGlobal:true/false, // 布尔值,更新可用时将显示的消息在默认消息的npm i建议中包括-g参数。boxenOptions:object,// 一个对象,传给boxen的参数}
      
解读index.js

引用包部分

  1. const {spawn} = require('child_process');
  • node.js中 杀死正在执行的子进程
  • const {spawn} = require('child_process');const spawn = require('child_process');的区别:
    1. {x} = v相当于{x:x} = v
    2. {x:y} = v相当于 y = v.x
    3. const {spawn} = require('child_process');相当于 const_ = require('child_process'); const spawn = _.resolve;
    4. 相当于解构出来的东西,在这个当前的文件里面有了这个东西,已经获取到了,可以直接使用,而不需要在通过spawn.xxxx 去拿相应的变量或者方法了
  1. const path = require('path');

node.js中 处理关于文件路径的问题链接

  1. const {format} = require('util');
  • util是常用的函数集合,用于弥补核心js功能过于精简的不足,为了满足nide.js 内部api 的需求
  • util.format(format[, …])
    1. util.form()函数接收 一个格式化字符串format作为第一个参数,并返回格式化后的字符串。
    2. format参数是可用包含零个或多个占位符的字符串,每个占位符都是以一个%字符开始,并最终被对应的参数转换的字符串值取代
  1. const importLazy = require('import-lazy')(require); usage
const importLazy = require('import-lazy')(require);//模块懒加载
const configstore = importLazy('configstore');// 轻松加载和持久化配置,而无需考虑在何处以及如何加载。
const chalk = importLazy('chalk'); //画彩色字体通知的
const semver = importLazy('semver'); //npm的语义版本器
const semverDiff = importLazy('semver-diff');// 获取两个semver版本的差异类型 //例如:0.0.1 0.0.2→ patch
const latestVersion = importLazy('latest-version');// 获取npm包的最新版本
const isNpm = importLazy('is-npm');//检查代码是不是作为npm 脚本运行
const isInstalledGlobally = importLazy('is-installed-globally'); //检查软件包是否是通过 npm 全局安装的
const isYarnGlobal = importLazy('is-yarn-global'); // 检查是否在没有任何fs调用的情况下由yarn全局安装
const hasYarn = importLazy('has-yarn'); //检查项目是否正在使用yarn
const boxen = importLazy('boxen');//在终端创建框
const xdgBasedir = importLazy('xdg-basedir');//获取XDG基本目录路径
const isCi = importLazy('is-ci');//如果当前环境是持续集成服务器,则返回true
const pupa = importLazy('pupa');//简易微模版const ONE_DAY = 1000 * 60 * 60 * 24; //24小时60分钟60秒1000毫秒
UpdateNotifier
class UpdateNotifier {constructor(options = {}) { }check(){}async fetchInfo() {}notify(options) {}
}
consructor初始化阶段
class UpdateNotifier {constructor(options = {}) {// 把package.json的东西给optionsthis.options = options;// 容错处理options.pkg = options.pkg || {};options.distTag = options.distTag || 'latest';// Reduce pkg to the essential keys. with fallback to deprecated options// TODO: Remove deprecated options at some point far into the futureoptions.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;/*** 通过读官方文档可以知道 updateCheckInterval 默认是一天(默认是数字格式) 此处检查周期,单位ms* const ONE_DAY = 1000 * 60 * 60 * 24; //24小时60分种60秒1000毫秒*/this.updateCheckInterval = typeof options.updateCheckInterval === 'number' ? options.updateCheckInterval : ONE_DAY;/*** isCi()  如果当前环境是持续集成服务器,则返回true* 禁用状态检测:是否禁止更新通知*/this.disabled = 'NO_UPDATE_NOTIFIER' in process.env ||process.env.NODE_ENV === 'test' ||process.argv.includes('--no-update-notifier') ||isCi();/*** shouldNotifyInNpmScript 是否允许在作为npm脚本运行时显示通知*/this.shouldNotifyInNpmScript = options.shouldNotifyInNpmScript;if (!this.disabled) {// 如果没有禁止更新通知try {//设置配置对象const ConfigStore = configstore();// 使用configstore获取本地该包相关信息的存储,第一次拿不到this.config = new ConfigStore(`update-notifier-${this.packageName}`, {optOut: false,// Init with the current time so the first check is only// after the set interval, so not to bother users right away// 设置当前时间为检查间隔的起点,不影响使用者  此时第一次启动时不通知的原因就在此处lastUpdateCheck: Date.now()});} catch {// Expecting error code EACCES or EPERM// 提示需错误信息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'}));});}}}check() {}async fetchInfo() {}notify(options) {}
}
check
	check() {/*** 如果获取不到本地该包相关信息的存储 || optOut 为 true || 禁止更新通知 的情况时停止检查*/if (!this.config || this.config.get('optOut') || this.disabled) {return;}//获取相关包的更新信息,第一次为undefinedthis.update = this.config.get('update');if (this.update) {// Use the real latest version instead of the cached one//如果获取到最新的包的信息了,使用实际值替代缓存值this.update.current = this.packageVersion;// Clear cached information 清除缓存的值this.config.delete('update');}// Only check for updates on a set interval//仅在设定的时间间隔内检查更新,如果还在周期内就返回,停止检查if (Date.now() - this.config.get('lastUpdateCheck') < this.updateCheckInterval) {return;}// Spawn a detached process, passing the options as an environment property/*** 生成分离的进程,将选项作为环境属性传递* path.join 将多个参数值合并成一个路径*/spawn(process.execPath, [path.join(__dirname, 'check.js'), JSON.stringify(this.options)], {//让子进程独立于其父进程运行子进程可以在父进程退出后继续运行,不管它们是否分离。[链接](http://nodejs.cn/api/child_process/options_detached.html)detached: true,// 不关注控制台的输入输出,忽略父进程的终止stdio: 'ignore'}).unref();// unref 方法用来断绝关系,这样“父”进程可以独立退出(不会导致子进程跟着退出)}
check.js
/* eslint-disable unicorn/no-process-exit */
'use strict';
// 获取index.js导出的内容
let updateNotifier = require('.');// 获取options
const options = JSON.parse(process.argv[2]);
//实例化 updateNotifier
updateNotifier = new updateNotifier.UpdateNotifier(options);//执行一个函数
(async () => {// Exit process when offline 脱机时退出进程setTimeout(process.exit, 1000 * 30);// 调用updateNotifier的fetchInfo方法获取更新信息const update = await updateNotifier.fetchInfo();// Only update the last update check time on success 仅在成功时更新上次更新的检查时间updateNotifier.config.set('lastUpdateCheck', Date.now());// 只要不是最新的包 就设置更新字段if (update.type && update.type !== 'latest') {updateNotifier.config.set('update', update);}// 获取到的信息,如果版本有更新,存到config// Call process exit explicitly to terminate the child process,// otherwise the child process will run forever, according to the Node.js docs// 显式调用进程退出以终止子进程,根据Node.js文档,否则子进程将永远运行process.exit();
})().catch(error => {console.error(error);process.exit(1);
});
fetchInfo 获取包的相关信息
	//获取包的相关信息async fetchInfo() {const {distTag} = this.options;// latestVersion 获取npm包的最新版本// 由于是懒加载的 所以需要先latestVersion() 执行下// 这里需要注意latestVersion是一个异步的请求const latest = await latestVersion()(this.packageName, {version: distTag});// 返回组装后的对象信息return {latest,current: this.packageVersion,type: semverDiff()(this.packageVersion, latest) || distTag,name: this.packageName};}notify(options) {}
notify 通知
	notify(options) {// 是否支持npm-scripts 调用const suppressForNpm = !this.shouldNotifyInNpmScript && isNpm().isNpmOrYarn;/*** !process.stdout.isTTY node不在终端上运行* suppressForNpm 不支持npm-scripts 调用* !this.update 本地没有检查过更新* !semver().gt(this.update.latest, this.update.current) 当前版本是否落后于最新版本* 满足以上条件时不需要通知(没有更新时不通知)*/if (!process.stdout.isTTY || suppressForNpm || !this.update || !semver().gt(this.update.latest, this.update.current)) {return this;}options = {// 检查软件包是否是通过 npm 全局安装的isGlobal: isInstalledGlobally(),// 是否是通过 yarn 全局安装的// 有个缺陷: 前提是用户没有改变yarn默认的全局安装目录isYarnGlobal: isYarnGlobal()(),...options};// 相应的更新命令提示let installCommand;if (options.isYarnGlobal) { // 是否是通过yarn全局安装installCommand = `yarn global add ${this.packageName}`;} else if (options.isGlobal) { //是否是通过npm 全局安装installCommand = `npm i -g ${this.packageName}`;} else if (hasYarn()()) {// hasYarn() 由于 hasYarn 是懒加载的模块,所以需要先执行一下 yarn 局部安装installCommand = `yarn add ${this.packageName}`;} else {// npm 局部安装installCommand = `npm i ${this.packageName}`;}const defaultTemplate = 'Update available ' +chalk().dim('{currentVersion}') +chalk().reset(' → ') +chalk().green('{latestVersion}') +' \nRun ' + chalk().cyan('{updateCommand}') + ' to update';//提示信息模板const template = options.message || defaultTemplate;options.boxenOptions = options.boxenOptions || {padding: 1,margin: 1,align: 'center',borderColor: 'yellow',borderStyle: 'round'};/*** boxen 在终端创建框* pupa 简易微模版*/const message = boxen()(pupa()(template, {// 将以下信息补全到模板中// this.update 是在 check 阶段填充的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的目的: 为了链式调用 option配置已改变return this;}
总结流程
  • const updateNotifier = new UpdateNotifier({})
    • 初始化 constuctor
    • updateNotifier.check()
  • 判断是否执行 check.js,如果执行:
    • updateNotifier.fetchInfo()
    • set(‘lastUpdateCheck’)
    • set(‘update’)
  • notify()
    在这里插入图片描述
总结

1.加深了对解构赋值的理解
2.根据源码知道了更多的node.js 的api
3.也知道本次的update-notifier更新是否是最新版本的包的工具库,可以加入到实际中使用
4.跟着官网去阅读也是个不错的顺序流程,尤其在集合各位大佬的笔记,理解起来会更加容易上手,收获满满,再接再厉!

  相关解决方案