Skip to content

Commit d709f66

Browse files
authored
feat: first impl (#1)
* feat: first impl * feat: add changelog * fix: print later * feat: default not check interval * feat: update name * feat: update version * docs: update readme
1 parent 3baa9b3 commit d709f66

21 files changed

+427
-55
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ coverage/
55
**/*.js
66
**/*.js.map
77
**/*.d.ts
8+
.npmcheckupdate

CHANGELOG.md

Whitespace-only changes.

README.md

+25-25
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
# artus-cli/template
1+
# artus-cli/plugin-npmcheckupdate
22

3-
template repository for artus-cli
3+
A artus-cli plugin be used to display upgrade info
44

5-
<!-- Badge,自行替换掉下面的 `artus-cli/artus-cli` 占位符-->
6-
7-
[![NPM version](https://img.shields.io/npm/v/@artus-cli/artus-cli.svg?style=flat-square)](https://npmjs.org/package/@artus-cli/artus-cli)
8-
[![NPM quality](https://img.shields.io/npms-io/final-score/@artus-cli/artus-cli.svg?style=flat-square)](https://npmjs.org/package/@artus-cli/artus-cli)
9-
[![NPM download](https://img.shields.io/npm/dm/@artus-cli/artus-cli.svg?style=flat-square)](https://npmjs.org/package/@artus-cli/artus-cli)
10-
[![Continuous Integration](https://github.com/artus-cli/artus-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/artus-cli/artus-cli/actions/workflows/ci.yml)
11-
[![Test coverage](https://img.shields.io/codecov/c/github/artus-cli/artus-cli.svg?style=flat-square)](https://codecov.io/gh/artus-cli/artus-cli)
12-
[![Oss Insight Analytics](https://img.shields.io/badge/OssInsight-artus--cli%2Fartus--cli-blue.svg?style=flat-square)](https://ossinsight.io/analyze/artus-cli/artus-cli)
5+
[![NPM version](https://img.shields.io/npm/v/@artus-cli/plugin-npmcheckupdate.svg?style=flat-square)](https://npmjs.org/package/@artus-cli/plugin-npmcheckupdate)
6+
[![NPM quality](https://img.shields.io/npms-io/final-score/@artus-cli/plugin-npmcheckupdate.svg?style=flat-square)](https://npmjs.org/package/@artus-cli/plugin-npmcheckupdate)
7+
[![NPM download](https://img.shields.io/npm/dm/@artus-cli/plugin-npmcheckupdate.svg?style=flat-square)](https://npmjs.org/package/@artus-cli/plugin-npmcheckupdate)
8+
[![Continuous Integration](https://github.com/artus-cli/plugin-npmcheckupdate/actions/workflows/ci.yml/badge.svg)](https://github.com/artus-cli/plugin-npmcheckupdate/actions/workflows/ci.yml)
9+
[![Test coverage](https://img.shields.io/codecov/c/github/artus-cli/plugin-npmcheckupdate.svg?style=flat-square)](https://codecov.io/gh/artus-cli/plugin-npmcheckupdate)
10+
[![Oss Insight Analytics](https://img.shields.io/badge/OssInsight-artus--cli%2Fartus--cli-blue.svg?style=flat-square)](https://ossinsight.io/analyze/artus-cli/plugin-npmcheckupdate)
1311

1412

1513
## Install
1614

1715
```sh
18-
$ npm i -D @artus-cli/artus-cli
16+
$ npm i -D @artus-cli/plugin-npmcheckupdate
1917
```
2018

2119
## Usage
@@ -26,22 +24,24 @@ $ npm i -D @artus-cli/artus-cli
2624
export default {
2725
template: {
2826
enable: true,
29-
package: 'artus-cli/artus-cli',
27+
package: 'artus-cli/plugin-npmcheckupdate',
3028
},
3129
};
3230
```
3331

34-
## Commands
35-
36-
### dev
37-
38-
39-
### debug
40-
32+
## Configuration
4133

42-
## Contributing
43-
44-
```sh
45-
$ npm test
46-
$ npm run cov
47-
```
34+
```ts
35+
export interface NpmCheckUpdateConfig {
36+
/** unpkg url, default is https://unpkg.com/ */
37+
unpkgUrl?: string;
38+
/** the file be used to cache last update info */
39+
cacheFile?: string;
40+
/** check inerval with local cache, default is 0, means no cache */
41+
checkInterval?: string | number;
42+
/** upgrade policy, canbe major/minor/patch or dist-tags, default is latest */
43+
upgradePolicy?: 'major' | 'minor' | 'patch' | DistTag;
44+
/** whether enable intercept in all command? default is true */
45+
enableInterceptor?: boolean | ((cmd: typeof Command) => boolean);
46+
}
47+
```

package.json

+17-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
2-
"name": "template-plugin",
3-
"version": "0.0.0",
4-
"description": "plugin template",
2+
"name": "@artus-cli/plugin-npmcheckupdate",
3+
"version": "0.0.2",
4+
"description": "A artus-cli plugin be used to display upgrade info",
55
"homepage": "",
6-
"author": "",
6+
"author": "whxaxes <[email protected]>",
77
"main": "dist/index.js",
88
"types": "dist/index.d.ts",
99
"type": "commonjs",
@@ -21,29 +21,38 @@
2121
"cov": "c8 -n src/ npm test",
2222
"ci": "npm run cov",
2323
"tsc": "rm -rf dist && tsc",
24-
"prepack": "npm run tsc"
24+
"prepack": "npm run tsc",
25+
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
2526
},
2627
"dependencies": {
27-
"tslib": "^2.4.0"
28+
"chalk": "^4.1.2",
29+
"debug": "^4.3.4",
30+
"ms": "^2.1.3",
31+
"ora": "^5.4.1",
32+
"semver": "^7.3.8",
33+
"tslib": "^2.4.0",
34+
"urllib": "^3.11.0"
2835
},
2936
"devDependencies": {
3037
"@artus-cli/artus-cli": "latest",
3138
"@artus/eslint-config-artus": "^0.0.1",
3239
"@artus/tsconfig": "^1",
3340
"@types/mocha": "^9.1.1",
34-
"@types/node": "^18.7.14",
41+
"@types/node": "^18.14.6",
3542
"c8": "^7.12.0",
3643
"coffee": "^5.5.0",
44+
"conventional-changelog": "^3.1.25",
3745
"eslint": "^8.28.0",
3846
"mocha": "^10.0.0",
47+
"rimraf": "^3.0.2",
3948
"ts-mocha": "^10.0.0",
4049
"ts-node": "^10.9.1",
4150
"tsconfig-paths": "^4.1.1",
4251
"typescript": "^4.8.2"
4352
},
4453
"repository": {
4554
"type": "git",
46-
"url": ""
55+
"url": "https://github.com/artus-cli/plugin-npmcheckupdate"
4756
},
4857
"files": [
4958
"dist"

src/cmd/example.ts

-11
This file was deleted.

src/config/config.default.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { NpmCheckUpdateConfig } from '../types';
2+
3+
export default {
4+
npmcheckupdate: {
5+
unpkgUrl: 'https://unpkg.com',
6+
checkInterval: 0,
7+
upgradePolicy: 'latest',
8+
enableInterceptor: true,
9+
} as NpmCheckUpdateConfig,
10+
};

src/helper.ts

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import chalk from 'chalk';
2+
import urllib from 'urllib';
3+
import assert from 'assert';
4+
import semver from 'semver';
5+
import { NpmCheckUpdateConfig, UpgradeInfo } from './types';
6+
import debug from 'debug';
7+
const helperDebug = debug('npmcheckupdate#helper');
8+
9+
export async function checkUpdate(option: Pick<NpmCheckUpdateConfig, 'unpkgUrl' | 'upgradePolicy'> & {
10+
pkgName: string;
11+
pkgVersion: string;
12+
pkgBaseDir: string;
13+
}) {
14+
const { unpkgUrl, upgradePolicy, pkgVersion, pkgName, pkgBaseDir } = option;
15+
16+
let distTag: string | undefined;
17+
let remoteVersionPatterns: string;
18+
const [ major, minor ] = pkgVersion.split('.');
19+
if (upgradePolicy === 'major') {
20+
remoteVersionPatterns = '*';
21+
} else if (upgradePolicy === 'minor') {
22+
remoteVersionPatterns = `${major}.*`;
23+
} else if (upgradePolicy === 'patch') {
24+
remoteVersionPatterns = `${major}.${minor}.*`;
25+
} else {
26+
distTag = upgradePolicy;
27+
remoteVersionPatterns = upgradePolicy;
28+
}
29+
30+
// request unpkg to get version info
31+
let remoteVersion: string;
32+
const unpkgHost = unpkgUrl.endsWith('/') ? unpkgUrl : `${unpkgUrl}/`;
33+
const remoteUrl = `${unpkgHost}${pkgName}@${remoteVersionPatterns}/package.json`;
34+
try {
35+
const { data } = await urllib.request(remoteUrl, {
36+
dataType: 'json',
37+
});
38+
39+
assert(data.version, 'Invalid package.json, not found version info');
40+
remoteVersion = data.version;
41+
} catch(e) {
42+
helperDebug(`Request ${remoteUrl} failed with error: ` + e.message);
43+
return undefined;
44+
}
45+
46+
// check whether need to update
47+
const result = semver.compare(remoteVersion, pkgVersion);
48+
if (result > 0) {
49+
return {
50+
distTag,
51+
upgradePolicy,
52+
pkgName,
53+
pkgVersion,
54+
pkgBaseDir,
55+
targetVersion: remoteVersion,
56+
};
57+
}
58+
59+
return undefined;
60+
}
61+
62+
export function displayUpgradeInfo(upgradeInfo: UpgradeInfo) {
63+
// update contents
64+
const contents = [
65+
`Update available ${chalk.gray(upgradeInfo.pkgVersion)}${chalk.greenBright(upgradeInfo.targetVersion)}`,
66+
`Run ${chalk.blueBright(`npm i -g ${upgradeInfo.pkgName}@${upgradeInfo.distTag || upgradeInfo.targetVersion}`)} to update`,
67+
];
68+
69+
const removeColor = c => c.replace(/\x1B\[\d+m/g, '');
70+
const maxLen = contents.map(removeColor)
71+
.sort((a, b) => a.length - b.length)
72+
.pop().length;
73+
74+
// show content
75+
const distanceLen = 2;
76+
const displayContents: string[] = [];
77+
const contentLen = maxLen + distanceLen * 2;
78+
displayContents.push('┌' + '─'.repeat(contentLen) + '┐');
79+
displayContents.push('│' + ' '.repeat(contentLen) + '│');
80+
displayContents.push('│' + ' '.repeat(contentLen) + '│');
81+
82+
contents.forEach(c => {
83+
const realContentLen = removeColor(c).length;
84+
const leftSpace = ' '.repeat(distanceLen + (maxLen - realContentLen) / 2);
85+
const rightSpace = ' '.repeat(contentLen - leftSpace.length - realContentLen);
86+
displayContents.push('│' + leftSpace + c + rightSpace + '│');
87+
});
88+
89+
displayContents.push('│' + ' '.repeat(contentLen) + '│');
90+
displayContents.push('│' + ' '.repeat(contentLen) + '│');
91+
displayContents.push('└' + '─'.repeat(contentLen) + '┘');
92+
console.info(`\n\n${displayContents.map(c => ` ${c}`).join('\n')}\n\n`);
93+
}

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from './cmd/example';
1+
export * from './types';

src/lifecycle.ts

+35-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,44 @@
1-
import { Program, Inject, ApplicationLifecycle, LifecycleHook, LifecycleHookUnit } from '@artus-cli/artus-cli';
1+
import { Program, Inject, ApplicationLifecycle, LifecycleHook, LifecycleHookUnit, CommandContext, ArtusInjectEnum } from '@artus-cli/artus-cli';
2+
import DefaultConfig from './config/config.default';
3+
import { UpgradeInfo } from './types';
4+
import { Updater } from './updater';
25

36
@LifecycleHookUnit()
4-
export default class TemplateLifecycle implements ApplicationLifecycle {
7+
export default class NpmCheckUpdateLifecycle implements ApplicationLifecycle {
58
@Inject()
69
private readonly program: Program;
710

11+
@Inject(ArtusInjectEnum.Config)
12+
config: typeof DefaultConfig;
13+
14+
@Inject()
15+
updater: Updater;
16+
817
@LifecycleHook()
918
async configDidLoad() {
10-
console.info('bin', this.program.binName);
19+
const { npmcheckupdate } = this.config;
20+
if (!npmcheckupdate.enableInterceptor) {
21+
return;
22+
}
23+
24+
this.program.use(async (ctx: CommandContext, next) => {
25+
let enableInterceptor = false;
26+
if (typeof npmcheckupdate.enableInterceptor === 'function') {
27+
enableInterceptor = ctx.matched ? npmcheckupdate.enableInterceptor(ctx.matched.clz) : false;
28+
} else {
29+
enableInterceptor = npmcheckupdate.enableInterceptor;
30+
}
31+
32+
let upgradeInfo: UpgradeInfo | undefined;
33+
if (enableInterceptor) {
34+
upgradeInfo = await this.updater.checkUpdate();
35+
}
36+
37+
await next();
38+
39+
if (upgradeInfo) {
40+
this.updater.displayUpgradeInfo(upgradeInfo);
41+
}
42+
});
1143
}
1244
}

src/meta.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"name": "template"
2+
"name": "npmcheckupdate"
33
}

src/types.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Command } from "@artus-cli/artus-cli";
2+
3+
export type DistTag = 'latest' | 'beta' | 'alpha' | string & {};
4+
5+
export interface NpmCheckUpdateConfig {
6+
/** unpkg url, default is https://unpkg.com/ */
7+
unpkgUrl?: string;
8+
/** the file be used to cache last update info */
9+
cacheFile?: string;
10+
/** check inerval with local cache, default is 0, means no cache */
11+
checkInterval?: string | number;
12+
/** upgrade policy, canbe major/minor/patch or dist-tags, default is latest */
13+
upgradePolicy?: 'major' | 'minor' | 'patch' | DistTag;
14+
/** whether enable intercept in all command? default is true */
15+
enableInterceptor?: boolean | ((cmd: typeof Command) => boolean);
16+
}
17+
18+
export interface CacheInfo {
19+
lastUpdateTime?: number;
20+
}
21+
22+
export interface UpgradeInfo {
23+
pkgName: string;
24+
pkgVersion: string;
25+
pkgBaseDir: string;
26+
distTag: DistTag;
27+
upgradePolicy: NpmCheckUpdateConfig['upgradePolicy'];
28+
targetVersion: string;
29+
}

0 commit comments

Comments
 (0)