@badisi/latest-version 2.0.5 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  📦 Get latest versions of packages.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@badisi/latest-version.svg?color=blue&logo=npm)][npm]
6
- [![npm downloads](https://img.shields.io/npm/dw/@badisi/latest-version.svg?color=blue&logo=npm)][npm-dl]
6
+ [![npm downloads](https://img.shields.io/npm/dw/@badisi/latest-version.svg?color=7986CB&logo=npm)][npm-dl]
7
7
  [![license](https://img.shields.io/npm/l/@badisi/latest-version.svg?color=ff69b4)][license]
8
8
 
9
9
  [![build status](https://github.com/badisi/latest-version/workflows/CI%20tests/badge.svg)][ci-tests]
@@ -11,34 +11,39 @@
11
11
  <!--[![dependencies status](https://img.shields.io/david/badisi/latest-version.svg)][deps]-->
12
12
  <!--[![devDependencies status](https://img.shields.io/david/dev/badisi/latest-version.svg)][dev-deps]-->
13
13
 
14
- <hr>
14
+ <hr/>
15
15
 
16
16
  ## Features
17
17
 
18
- * Get latest versions of packages *(from package registries)*
19
- * `latest`, `next` and `wanted` if a version range or a tag is provided
20
- * Get `installed` version of packages *(if installed locally or globally)*
21
- * Check if `updates` are available
22
- * Cache support to increase data retrieval performance
23
- * Support public/private repositories and proxies
18
+ Get `latest` and `next` versions of packages *(from package registries)*<br/>
19
+ Get `wanted` version of packages *(if a version range or a tag is provided)*<br/>
20
+ Get `installed` version of packages *(if installed locally or globally)*<br/>
21
+ Check if `updates` are available<br/>
22
+ Cache support to increase data retrieval performance<br/>
23
+ Support public/private repositories and proxies<br/>
24
24
 
25
25
  ## Installation
26
26
 
27
27
  ```sh
28
- $ npm install @badisi/latest-version --save
28
+ npm install @badisi/latest-version --save
29
29
  ```
30
30
 
31
31
  ```sh
32
- $ yarn add @badisi/latest-version
32
+ yarn add @badisi/latest-version
33
33
  ```
34
34
 
35
35
  ## Usage
36
36
 
37
37
  __Example__
38
38
 
39
- ```js
40
- const { readFileSync } = require('fs');
41
- const latestVersion = require('@badisi/latest-version');
39
+ ```ts
40
+ /** CommonJS */
41
+ // const { readFileSync } = require('fs');
42
+ // const latestVersion = require('@badisi/latest-version');
43
+
44
+ /** ESM / Typescript */
45
+ import { readFileSync } from 'fs';
46
+ import latestVersion from '@badisi/latest-version';
42
47
 
43
48
  (async () => {
44
49
  // Single package
@@ -159,13 +164,13 @@ See the [developer docs][developer].
159
164
 
160
165
  ## Contributing
161
166
 
162
- ### Want to Help ?
167
+ #### > Want to Help ?
163
168
 
164
169
  Want to file a bug, contribute some code or improve documentation ? Excellent!
165
170
 
166
171
  But please read up first on the guidelines for [contributing][contributing], and learn about submission process, coding rules and more.
167
172
 
168
- ### Code of Conduct
173
+ #### > Code of Conduct
169
174
 
170
175
  Please read and follow the [Code of Conduct][codeofconduct] and help me keep this project open and inclusive.
171
176
 
@@ -177,8 +182,8 @@ Please read and follow the [Code of Conduct][codeofconduct] and help me keep thi
177
182
  [ci-tests]: https://github.com/badisi/latest-version/actions?query=workflow:CI%20tests
178
183
  [deps]: https://david-dm.org/badisi/latest-version
179
184
  [dev-deps]: https://david-dm.org/badisi/latest-version?type=dev
180
- [pullrequest]: https://github.com/badisi/latest-version/blob/master/CONTRIBUTING.md#-submitting-a-pull-request-pr
181
- [license]: https://github.com/badisi/latest-version/blob/master/LICENSE
182
- [developer]: https://github.com/badisi/latest-version/blob/master/DEVELOPER.md
183
- [contributing]: https://github.com/badisi/latest-version/blob/master/CONTRIBUTING.md
184
- [codeofconduct]: https://github.com/badisi/latest-version/blob/master/CODE_OF_CONDUCT.md
185
+ [pullrequest]: https://github.com/badisi/latest-version/blob/main/CONTRIBUTING.md#-submitting-a-pull-request-pr
186
+ [license]: https://github.com/badisi/latest-version/blob/main/LICENSE
187
+ [developer]: https://github.com/badisi/latest-version/blob/main/DEVELOPER.md
188
+ [contributing]: https://github.com/badisi/latest-version/blob/main/CONTRIBUTING.md
189
+ [codeofconduct]: https://github.com/badisi/latest-version/blob/main/CODE_OF_CONDUCT.md
File without changes
@@ -32,7 +32,7 @@ const downloadMetadata = (pkgName, options) => {
32
32
  requestOptions.headers.authorization = `${authInfo.type} ${authInfo.token}`;
33
33
  }
34
34
  if (options === null || options === void 0 ? void 0 : options.requestOptions) {
35
- requestOptions = { ...requestOptions, ...options.requestOptions };
35
+ requestOptions = Object.assign(Object.assign({}, requestOptions), options.requestOptions);
36
36
  }
37
37
  const { get } = require((pkgUrl.protocol === 'https:') ? 'https' : 'http');
38
38
  const request = get(requestOptions, (res) => {
@@ -98,7 +98,7 @@ const getMetadataFromCache = (pkgName, options) => {
98
98
  }
99
99
  return undefined;
100
100
  };
101
- const getLatestVersions = async (pkgName, tagOrRange, options) => {
101
+ const getLatestVersions = (pkgName, tagOrRange, options) => (0, tslib_1.__awaiter)(void 0, void 0, void 0, function* () {
102
102
  var _a, _b, _c;
103
103
  let pkgMetadata;
104
104
  if (pkgName.length && (options === null || options === void 0 ? void 0 : options.useCache)) {
@@ -108,7 +108,7 @@ const getLatestVersions = async (pkgName, tagOrRange, options) => {
108
108
  }
109
109
  }
110
110
  else if (pkgName.length) {
111
- pkgMetadata = await downloadMetadata(pkgName, options);
111
+ pkgMetadata = yield downloadMetadata(pkgName, options);
112
112
  }
113
113
  const versions = {
114
114
  latest: (_a = pkgMetadata === null || pkgMetadata === void 0 ? void 0 : pkgMetadata.distTags) === null || _a === void 0 ? void 0 : _a.latest,
@@ -121,18 +121,18 @@ const getLatestVersions = async (pkgName, tagOrRange, options) => {
121
121
  versions.wanted = (0, semver_1.maxSatisfying)(pkgMetadata.versions, tagOrRange) || undefined;
122
122
  }
123
123
  return versions;
124
- };
124
+ });
125
125
  const getInstalledVersion = (pkgName) => {
126
126
  var _a;
127
127
  try {
128
128
  const paths = ['.', global_dirs_1.npm.packages, global_dirs_1.yarn.packages];
129
129
  return (_a = require(require.resolve((0, path_1.join)(pkgName, 'package.json'), { paths }))) === null || _a === void 0 ? void 0 : _a.version;
130
130
  }
131
- catch {
131
+ catch (_b) {
132
132
  return undefined;
133
133
  }
134
134
  };
135
- const getInfo = async (pkg, options) => {
135
+ const getInfo = (pkg, options) => (0, tslib_1.__awaiter)(void 0, void 0, void 0, function* () {
136
136
  const i = pkg.lastIndexOf('@');
137
137
  let pkgInfo = {
138
138
  name: (i > 1) ? pkg.slice(0, i) : pkg,
@@ -140,11 +140,7 @@ const getInfo = async (pkg, options) => {
140
140
  updatesAvailable: { latest: false, next: false, wanted: false }
141
141
  };
142
142
  try {
143
- pkgInfo = {
144
- ...pkgInfo,
145
- installed: getInstalledVersion(pkgInfo.name),
146
- ...(await getLatestVersions(pkgInfo.name, pkgInfo.wantedTagOrRange, options))
147
- };
143
+ pkgInfo = Object.assign(Object.assign(Object.assign({}, pkgInfo), { installed: getInstalledVersion(pkgInfo.name) }), (yield getLatestVersions(pkgInfo.name, pkgInfo.wantedTagOrRange, options)));
148
144
  pkgInfo.updatesAvailable = {
149
145
  latest: (pkgInfo.installed && pkgInfo.latest) ? (0, semver_1.gt)(pkgInfo.latest, pkgInfo.installed) : false,
150
146
  next: (pkgInfo.installed && pkgInfo.next) ? (0, semver_1.gt)(pkgInfo.next, pkgInfo.installed) : false,
@@ -155,8 +151,8 @@ const getInfo = async (pkg, options) => {
155
151
  pkgInfo.error = (err === null || err === void 0 ? void 0 : err.message) || err;
156
152
  }
157
153
  return pkgInfo;
158
- };
159
- const latestVersion = async (arg, options) => {
154
+ });
155
+ const latestVersion = (arg, options) => (0, tslib_1.__awaiter)(void 0, void 0, void 0, function* () {
160
156
  const pkgs = [];
161
157
  if (typeof arg === 'string') {
162
158
  pkgs.push(arg);
@@ -174,9 +170,9 @@ const latestVersion = async (arg, options) => {
174
170
  addDeps(arg.devDependencies);
175
171
  addDeps(arg.peerDependencies);
176
172
  }
177
- const jobs = await Promise.allSettled(pkgs.map((pkg) => getInfo(pkg, options)));
173
+ const jobs = yield Promise.allSettled(pkgs.map((pkg) => getInfo(pkg, options)));
178
174
  const results = jobs.map((jobResult) => jobResult.value);
179
175
  return (typeof arg === 'string') ? results[0] : results;
180
- };
176
+ });
181
177
  exports.default = latestVersion;
182
178
  module.exports = latestVersion;
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
package/esm/index.d.ts ADDED
@@ -0,0 +1,142 @@
1
+ /// <reference types="node" />
2
+ import { Agent } from 'http';
3
+ interface LatestVersions {
4
+ /**
5
+ * The latest version of the package found on the provided registry (if found).
6
+ */
7
+ latest?: string;
8
+ /**
9
+ * The next version of the package found on the provided registry (if found).
10
+ */
11
+ next?: string;
12
+ /**
13
+ * The latest version of the package found on the provided registry and satisfied by the provided tag or version range (if provided).
14
+ */
15
+ wanted?: string;
16
+ }
17
+ interface LatestVersionPackage extends LatestVersions {
18
+ /**
19
+ * The name of the package.
20
+ */
21
+ name: string;
22
+ /**
23
+ * The current local or global installed version of the package (if installed).
24
+ */
25
+ installed?: string;
26
+ /**
27
+ * The tag or version range that was provided (if provided).
28
+ */
29
+ wantedTagOrRange?: string;
30
+ /**
31
+ * Whether the installed version (if any) could be upgraded or not.
32
+ */
33
+ updatesAvailable: {
34
+ latest: boolean;
35
+ next: boolean;
36
+ wanted: boolean;
37
+ };
38
+ /**
39
+ * Any error that might have occurred during the process.
40
+ */
41
+ error?: Error;
42
+ }
43
+ interface RequestOptions {
44
+ readonly ca?: string | Buffer | Array<string | Buffer>;
45
+ readonly rejectUnauthorized?: boolean;
46
+ readonly agent?: Agent | boolean;
47
+ readonly timeout?: number;
48
+ }
49
+ interface LatestVersionOptions {
50
+ /**
51
+ * Awaiting the api to return might take time, depending on the network, and might impact your package loading performance.
52
+ * You can use the cache mechanism to improve load performance and reduce unnecessary network requests.
53
+ * If `useCache` is not supplied, the api will always check for updates and wait for every requests to return before returning itself.
54
+ * If `useCache` is used, the api will always returned immediately, with either (for each provided packages):
55
+ * 1) a latest/next version available if a cache was found
56
+ * 2) no latest/next version available if no cache was found - in such case updates will be fetched in the background and a cache will
57
+ * be created for each provided packages and made available for the next call to the api.
58
+ *
59
+ * @default false
60
+ */
61
+ readonly useCache?: boolean;
62
+ /**
63
+ * How long the cache for the provided packages should be used before being refreshed (in milliseconds).
64
+ * If `useCache` is not supplied, this option has no effect.
65
+ * If `0` is used, this will force the cache to refresh immediately:
66
+ * 1) The api will returned immediately (without any latest nor next version available for the provided packages)
67
+ * 2) New updates will be fetched in the background
68
+ * 3) The cache for each provided packages will be refreshed and made available for the next call to the api
69
+ *
70
+ * @default ONE_DAY
71
+ */
72
+ readonly cacheMaxAge?: number;
73
+ /**
74
+ * A JavaScript package registry url that implements the CommonJS Package Registry specification.
75
+ *
76
+ * @default "Looks at any registry urls in the .npmrc file or fallback to the default npm registry instead"
77
+ * @example <caption>.npmrc</caption>
78
+ * registry = 'https://custom-registry.com/'
79
+ * @pkgscope:registry = 'https://custom-registry.com/'
80
+ */
81
+ readonly registryUrl?: string;
82
+ /**
83
+ * Set of options to be passed down to Node.js http/https request.
84
+ *
85
+ * @example <caption>Behind a proxy with self-signed certificate</caption>
86
+ * { ca: [ fs.readFileSync('proxy-cert.pem') ] }
87
+ * @example <caption>Bypassing certificate validation</caption>
88
+ * { rejectUnauthorized: false }
89
+ */
90
+ readonly requestOptions?: RequestOptions;
91
+ }
92
+ declare type LatestVersion = {
93
+ /**
94
+ * Get latest versions of packages from of a package json like object.
95
+ *
96
+ * @param {PackageJson} item - A package json like object (with dependencies, devDependencies and peerDependencies attributes).
97
+ * @example { dependencies: { 'npm': 'latest' }, devDependencies: { 'npm': '1.3.2' }, peerDependencies: { '@scope/name': '^5.0.2' } }
98
+ * @param {LatestVersionOptions} [options] - An object optionally specifying the use of the cache, the max age of the cache, the registry url and the http or https options.
99
+ * If `useCache` is not supplied, the default of `false` is used.
100
+ * If `cacheMaxAge` is not supplied, the default of `one day` is used.
101
+ * If `registryUrl` is not supplied, the default from `.npmrc` is used or a fallback to the `npm registry url` instead.
102
+ * @returns {Promise<LatestVersionPackage[]>}
103
+ */
104
+ (item: PackageJson, options?: LatestVersionOptions): Promise<LatestVersionPackage[]>;
105
+ /**
106
+ * Get latest version of a single package.
107
+ *
108
+ * @param {Package} item - A single package object (represented by a string that should match the following format: `${'@' | ''}${string}@${string}`)
109
+ * @example 'npm', 'npm@1.3.2', '@scope/name@^5.0.2'
110
+ * @param {LatestVersionOptions} [options] - An object optionally specifying the use of the cache, the max age of the cache, the registry url and the http or https options.
111
+ * If `useCache` is not supplied, the default of `false` is used.
112
+ * If `cacheMaxAge` is not supplied, the default of `one day` is used.
113
+ * If `registryUrl` is not supplied, the default from `.npmrc` is used or a fallback to the npm registry url instead.
114
+ * @returns {Promise<LatestVersionPackage>}
115
+ */
116
+ (item: Package, options?: LatestVersionOptions): Promise<LatestVersionPackage>;
117
+ /**
118
+ * Get latest versions of a collection of packages.
119
+ *
120
+ * @param {Package[]} items - A collection of package object (represented by a string that should match the following format: `${'@' | ''}${string}@${string}`)
121
+ * @example ['npm', 'npm@1.3.2', '@scope/name@^5.0.2']
122
+ * @param {LatestVersionOptions} [options] - An object optionally specifying the use of the cache, the max age of the cache, the registry url and the http or https options.
123
+ * If `useCache` is not supplied, the default of `false` is used.
124
+ * If `cacheMaxAge` is not supplied, the default of `one day` is used.
125
+ * If `registryUrl` is not supplied, the default from `.npmrc` is used or a fallback to the npm registry url instead.
126
+ * @returns {Promise<LatestVersionPackage[]>}
127
+ */
128
+ (items: Package[], options?: LatestVersionOptions): Promise<LatestVersionPackage[]>;
129
+ };
130
+ declare type PackageRange = `${'@' | ''}${string}@${string}`;
131
+ declare type Package = string | PackageRange;
132
+ declare type PackageJsonDependencies = Record<string, string>;
133
+ declare type PackageJson = Record<string, any> & ({
134
+ dependencies: PackageJsonDependencies;
135
+ } | {
136
+ devDependencies: PackageJsonDependencies;
137
+ } | {
138
+ peerDependencies: PackageJsonDependencies;
139
+ });
140
+ declare const latestVersion: LatestVersion;
141
+ export { LatestVersion, Package, PackageRange, PackageJson, PackageJsonDependencies, LatestVersions, LatestVersionPackage, RequestOptions, LatestVersionOptions };
142
+ export default latestVersion;
package/esm/index.js ADDED
@@ -0,0 +1,177 @@
1
+ import getRegistryUrl from 'registry-auth-token/registry-url';
2
+ import registryAuthToken from 'registry-auth-token';
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
4
+ import { gt, maxSatisfying } from 'semver';
5
+ import { npm, yarn } from 'global-dirs';
6
+ import { join, dirname } from 'path';
7
+ import { homedir } from 'os';
8
+ import { URL } from 'url';
9
+ const ONE_DAY = 1000 * 60 * 60 * 24; // eslint-disable-line @typescript-eslint/naming-convention
10
+ const isPackageJson = (obj) => {
11
+ return (obj.dependencies !== undefined) ||
12
+ (obj.devDependencies !== undefined) ||
13
+ (obj.peerDependencies !== undefined);
14
+ };
15
+ const downloadMetadata = (pkgName, options) => {
16
+ return new Promise((resolve, reject) => {
17
+ const i = pkgName.indexOf('/');
18
+ const pkgScope = (i !== -1) ? pkgName.slice(0, i) : '';
19
+ const registryUrl = options?.registryUrl || getRegistryUrl(pkgScope);
20
+ const pkgUrl = new URL(encodeURIComponent(pkgName).replace(/^%40/, '@'), registryUrl);
21
+ let requestOptions = {
22
+ headers: { accept: 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*' },
23
+ host: pkgUrl.hostname,
24
+ path: pkgUrl.pathname,
25
+ port: pkgUrl.port
26
+ };
27
+ const authInfo = registryAuthToken(pkgUrl.toString(), { recursive: true });
28
+ if (authInfo && requestOptions.headers) {
29
+ requestOptions.headers.authorization = `${authInfo.type} ${authInfo.token}`;
30
+ }
31
+ if (options?.requestOptions) {
32
+ requestOptions = { ...requestOptions, ...options.requestOptions };
33
+ }
34
+ const { get } = require((pkgUrl.protocol === 'https:') ? 'https' : 'http');
35
+ const request = get(requestOptions, (res) => {
36
+ if (res.statusCode === 200) {
37
+ let rawData = '';
38
+ res.setEncoding('utf8');
39
+ res.on('data', (chunk) => rawData += chunk);
40
+ res.once('end', () => {
41
+ res.setTimeout(0);
42
+ res.removeAllListeners();
43
+ try {
44
+ const pkgMetadata = JSON.parse(rawData);
45
+ return resolve({
46
+ name: pkgName,
47
+ lastUpdateDate: Date.now(),
48
+ versions: Object.keys(pkgMetadata.versions),
49
+ distTags: pkgMetadata['dist-tags']
50
+ });
51
+ }
52
+ catch (err) {
53
+ return reject(err);
54
+ }
55
+ });
56
+ }
57
+ else {
58
+ res.removeAllListeners();
59
+ res.resume(); // consume response data to free up memory
60
+ return reject(`Request error (${res.statusCode}): ${pkgUrl}`);
61
+ }
62
+ });
63
+ const abort = (error) => {
64
+ request.removeAllListeners();
65
+ request.destroy();
66
+ return reject(error);
67
+ };
68
+ request.once('timeout', () => abort(`Request timed out: ${pkgUrl}`));
69
+ request.once('error', (err) => abort(err));
70
+ });
71
+ };
72
+ const getCacheDir = (name = '@badisi/latest-version') => {
73
+ const homeDir = homedir();
74
+ switch (process.platform) {
75
+ case 'darwin': return join(homeDir, 'Library', 'Caches', name);
76
+ case 'win32': return join(process.env.LOCALAPPDATA || join(homeDir, 'AppData', 'Local'), name, 'Cache');
77
+ default: return join(process.env.XDG_CACHE_HOME || join(homeDir, '.cache'), name);
78
+ }
79
+ };
80
+ const saveMetadataToCache = (pkg) => {
81
+ const filePath = join(getCacheDir(), `${pkg.name}.json`);
82
+ if (!existsSync(dirname(filePath))) {
83
+ mkdirSync(dirname(filePath), { recursive: true });
84
+ }
85
+ writeFileSync(filePath, JSON.stringify(pkg));
86
+ };
87
+ const getMetadataFromCache = (pkgName, options) => {
88
+ const pkgCacheFilePath = join(getCacheDir(), `${pkgName}.json`);
89
+ if (existsSync(pkgCacheFilePath)) {
90
+ const pkg = JSON.parse(readFileSync(pkgCacheFilePath).toString());
91
+ const maxAge = (options?.cacheMaxAge !== undefined) ? options.cacheMaxAge : ONE_DAY;
92
+ if ((Date.now() - pkg.lastUpdateDate) < maxAge) {
93
+ return pkg;
94
+ }
95
+ }
96
+ return undefined;
97
+ };
98
+ const getLatestVersions = async (pkgName, tagOrRange, options) => {
99
+ let pkgMetadata;
100
+ if (pkgName.length && options?.useCache) {
101
+ pkgMetadata = getMetadataFromCache(pkgName, options);
102
+ if (!pkgMetadata) {
103
+ return downloadMetadata(pkgName, options).then(saveMetadataToCache);
104
+ }
105
+ }
106
+ else if (pkgName.length) {
107
+ pkgMetadata = await downloadMetadata(pkgName, options);
108
+ }
109
+ const versions = {
110
+ latest: pkgMetadata?.distTags?.latest,
111
+ next: pkgMetadata?.distTags?.next
112
+ };
113
+ if (tagOrRange && pkgMetadata?.distTags && pkgMetadata?.distTags[tagOrRange]) {
114
+ versions.wanted = pkgMetadata.distTags[tagOrRange];
115
+ }
116
+ else if (tagOrRange && pkgMetadata?.versions?.length) {
117
+ versions.wanted = maxSatisfying(pkgMetadata.versions, tagOrRange) || undefined;
118
+ }
119
+ return versions;
120
+ };
121
+ const getInstalledVersion = (pkgName) => {
122
+ try {
123
+ const paths = ['.', npm.packages, yarn.packages];
124
+ return require(require.resolve(join(pkgName, 'package.json'), { paths }))?.version;
125
+ }
126
+ catch {
127
+ return undefined;
128
+ }
129
+ };
130
+ const getInfo = async (pkg, options) => {
131
+ const i = pkg.lastIndexOf('@');
132
+ let pkgInfo = {
133
+ name: (i > 1) ? pkg.slice(0, i) : pkg,
134
+ wantedTagOrRange: (i > 1) ? pkg.slice(i + 1) : undefined,
135
+ updatesAvailable: { latest: false, next: false, wanted: false }
136
+ };
137
+ try {
138
+ pkgInfo = {
139
+ ...pkgInfo,
140
+ installed: getInstalledVersion(pkgInfo.name),
141
+ ...(await getLatestVersions(pkgInfo.name, pkgInfo.wantedTagOrRange, options))
142
+ };
143
+ pkgInfo.updatesAvailable = {
144
+ latest: (pkgInfo.installed && pkgInfo.latest) ? gt(pkgInfo.latest, pkgInfo.installed) : false,
145
+ next: (pkgInfo.installed && pkgInfo.next) ? gt(pkgInfo.next, pkgInfo.installed) : false,
146
+ wanted: (pkgInfo.installed && pkgInfo.wanted) ? gt(pkgInfo.wanted, pkgInfo.installed) : false
147
+ };
148
+ }
149
+ catch (err) {
150
+ pkgInfo.error = err?.message || err;
151
+ }
152
+ return pkgInfo;
153
+ };
154
+ const latestVersion = async (arg, options) => {
155
+ const pkgs = [];
156
+ if (typeof arg === 'string') {
157
+ pkgs.push(arg);
158
+ }
159
+ else if (Array.isArray(arg)) {
160
+ pkgs.push(...arg);
161
+ }
162
+ else if (isPackageJson(arg)) {
163
+ const addDeps = (deps) => {
164
+ if (deps) {
165
+ pkgs.push(...Object.keys(deps).map((key) => `${key}@${deps[key]}`));
166
+ }
167
+ };
168
+ addDeps(arg.dependencies);
169
+ addDeps(arg.devDependencies);
170
+ addDeps(arg.peerDependencies);
171
+ }
172
+ const jobs = await Promise.allSettled(pkgs.map((pkg) => getInfo(pkg, options)));
173
+ const results = jobs.map((jobResult) => jobResult.value);
174
+ return (typeof arg === 'string') ? results[0] : results;
175
+ };
176
+ export default latestVersion;
177
+ module.exports = latestVersion;
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/package.json CHANGED
@@ -1,14 +1,21 @@
1
1
  {
2
2
  "name": "@badisi/latest-version",
3
- "version": "2.0.5",
3
+ "version": "2.1.2",
4
4
  "description": "Get latest versions of packages",
5
5
  "homepage": "https://github.com/badisi/latest-version",
6
6
  "license": "MIT",
7
7
  "author": {
8
8
  "name": "Badisi"
9
9
  },
10
- "main": "./index.js",
11
- "types": "./index.d.ts",
10
+ "main": "cjs/index.js",
11
+ "module": "esm/index.js",
12
+ "types": "esm/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "require": "./cjs/index.js",
16
+ "import": "./esm/index.js"
17
+ }
18
+ },
12
19
  "repository": {
13
20
  "type": "git",
14
21
  "url": "https://github.com/badisi/latest-version.git"
@@ -31,5 +38,8 @@
31
38
  "global-dirs": "^3.0.0",
32
39
  "registry-auth-token": "^4.2.1",
33
40
  "semver": "^7.3.5"
41
+ },
42
+ "engines": {
43
+ "node": ">= 12"
34
44
  }
35
45
  }