npm-check-updates 7.0.4-alpha.2 → 7.1.1

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
@@ -111,7 +111,7 @@ $ ncu "/^(?!gulp-).*$/" # windows
111
111
  satisfied by the existing version range (v2 behavior).
112
112
  -n, --newest find the newest published versions available instead
113
113
  of the latest stable versions
114
- -p, --packageManager npm (default: npm)
114
+ -p, --packageManager npm|yarn (default: npm)
115
115
  --packageData include stringified package file (use stdin instead)
116
116
  --packageFile package file location (default: ./package.json)
117
117
  --pre include -alpha, -beta, -rc. (default: 0; default
package/lib/logging.js CHANGED
@@ -33,6 +33,12 @@ function print(options, message, loglevel, method = 'log') {
33
33
  }
34
34
  }
35
35
 
36
+ function printJson(options, object) {
37
+ if (options.loglevel !== 'silent') {
38
+ console.log(JSON.stringify(object, null, 2))
39
+ }
40
+ }
41
+
36
42
  function createDependencyTable() {
37
43
  return new Table({
38
44
  colAligns: ['left', 'right', 'right', 'right'],
@@ -73,4 +79,4 @@ function toDependencyTable(args) {
73
79
  return table
74
80
  }
75
81
 
76
- module.exports = { print, toDependencyTable }
82
+ module.exports = { print, printJson, toDependencyTable }
@@ -16,11 +16,12 @@ const { rcFile } = require('rc-config-loader')
16
16
  const jph = require('json-parse-helpfulerror')
17
17
  const vm = require('./versionmanager')
18
18
  const packageManagers = require('./package-managers')
19
- const { print, toDependencyTable } = require('./logging')
19
+ const { print, printJson, toDependencyTable } = require('./logging')
20
20
 
21
21
  // maps package managers to package file names
22
22
  const packageFileNames = {
23
- npm: 'package.json'
23
+ npm: 'package.json',
24
+ yarn: 'package.json',
24
25
  }
25
26
 
26
27
  // time to wait for stdin before printing a warning
@@ -41,18 +42,12 @@ function programError(options, message) {
41
42
  }
42
43
  }
43
44
 
44
- function printJson(options, object) {
45
- if (options.loglevel !== 'silent') {
46
- console.log(JSON.stringify(object, null, 2))
47
- }
48
- }
49
-
50
45
  /**
51
46
  * Gets the name of the package file based on --packageFile or --packageManager.
52
47
  */
53
48
  function getPackageFileName(options) {
54
49
  return options.packageFile ? options.packageFile :
55
- packageFileNames[options.packageManager]
50
+ packageFileNames[options.packageManager] || packageFileNames.npm
56
51
  }
57
52
 
58
53
  const readPackageFile = _.partialRight(promisify(fs.readFile), 'utf8')
@@ -94,17 +89,18 @@ function analyzeGlobalPackages(options) {
94
89
  total: upgradedPackageNames.length
95
90
  })
96
91
 
97
- let instruction = '[package]'
98
- if (upgraded) {
99
- instruction = upgradedPackageNames.map(pkg => pkg + '@' + upgraded[pkg]).join(' ')
100
- }
92
+ const instruction = upgraded
93
+ ? upgradedPackageNames.map(pkg => pkg + '@' + upgraded[pkg]).join(' ')
94
+ : '[package]'
101
95
 
102
96
  if (options.json) {
103
97
  // since global packages do not have a package.json, return the upgraded deps directly (no version range replacements)
104
98
  printJson(options, upgraded)
105
99
  }
106
100
  else if (instruction.length) {
107
- print(options, '\n' + chalk.cyan('ncu') + ' itself cannot upgrade global packages. Run the following to upgrade all global packages: \n\n' + chalk.cyan('npm -g install ' + instruction) + '\n')
101
+ const upgradeCmd = options.packageManager === 'yarn' ? 'yarn global upgrade' : 'npm -g install'
102
+
103
+ print(options, '\n' + chalk.cyan('ncu') + ' itself cannot upgrade global packages. Run the following to upgrade all global packages: \n\n' + chalk.cyan(`${upgradeCmd} ` + instruction) + '\n')
108
104
  }
109
105
  })
110
106
  })
@@ -387,6 +383,10 @@ async function run(options = {}) {
387
383
  options.prefix = await packageManagers.npm.defaultPrefix(options)
388
384
  }
389
385
 
386
+ if (options.packageManager === 'yarn' && !options.prefix) {
387
+ options.prefix = await packageManagers.yarn.defaultPrefix(options)
388
+ }
389
+
390
390
  let timeout
391
391
  let timeoutPromise = new Promise(resolve => resolve)
392
392
  if (options.timeout) {
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  module.exports = {
4
- npm: require('./npm')
4
+ npm: require('./npm'),
5
+ yarn: require('./yarn')
5
6
  }
@@ -0,0 +1,336 @@
1
+ 'use strict'
2
+
3
+ // eslint-disable-next-line fp/no-events
4
+ const { once } = require('events')
5
+
6
+ const _ = require('lodash')
7
+ const cint = require('cint')
8
+ const semver = require('semver')
9
+ const spawn = require('spawn-please')
10
+ const pacote = require('pacote')
11
+ const libnpmconfig = require('libnpmconfig')
12
+
13
+ const jsonlines = require('jsonlines')
14
+
15
+ const versionUtil = require('../version-util')
16
+
17
+ const TIME_FIELDS = ['modified', 'created']
18
+
19
+ // needed until pacote supports full yarn config compatibility
20
+ // See: https://github.com/zkat/pacote/issues/156
21
+ const yarnConfig = {}
22
+ libnpmconfig.read().forEach((value, key) => {
23
+ // replace env ${VARS} in strings with the process.env value
24
+ yarnConfig[key] = typeof value !== 'string' ?
25
+ value :
26
+ value.replace(/\${([^}]+)}/, (_, envVar) =>
27
+ process.env[envVar]
28
+ )
29
+ })
30
+ yarnConfig.cache = false
31
+
32
+ /**
33
+ * @typedef {object} CommandAndPackageName
34
+ * @property {string} command
35
+ * @property {string} packageName
36
+ */
37
+
38
+ /**
39
+ * Parse JSON lines and throw an informative error on failure.
40
+ *
41
+ * @param result Output from `yarn list --json` to be parsed
42
+ */
43
+ async function parseJsonLines(result) {
44
+
45
+ const dependencies = {}
46
+
47
+ const parser = jsonlines.parse()
48
+
49
+ parser.on('data', d => {
50
+ // only parse info data
51
+ // ignore error info, e.g. "Visit https://yarnpkg.com/en/docs/cli/list for documentation about this command."
52
+ if (d.type === 'info' && !d.data.match(/^Visit/)) {
53
+
54
+ // parse package name and version number from info data, e.g. "nodemon@2.0.4" has binaries
55
+ const [, pkgName, pkgVersion] = d.data.match(/"(@?.*)@(.*)"/)
56
+
57
+ dependencies[pkgName] = {
58
+ version: pkgVersion,
59
+ from: pkgName,
60
+ }
61
+
62
+ }
63
+ else if (d.type === 'error') {
64
+ throw new Error(d.data)
65
+ }
66
+ })
67
+
68
+ parser.write(result)
69
+
70
+ parser.end()
71
+
72
+ await once(parser, 'end')
73
+
74
+ return { dependencies }
75
+
76
+ }
77
+
78
+ /**
79
+ * Returns the value of one of the properties retrieved by yarn view.
80
+ *
81
+ * @param packageName Name of the package
82
+ * @param field Field such as "versions" or "dist-tags.latest" are parsed from the pacote result (https://www.yarnjs.com/package/pacote#packument)
83
+ * @param currentVersion
84
+ * @returns Promised result
85
+ */
86
+ function viewOne(packageName, field, currentVersion, { timeout } = {}) {
87
+ return viewMany(packageName, [field], currentVersion, { timeout })
88
+ .then(result => result && result[field])
89
+ }
90
+
91
+ /**
92
+ * Returns an object of specified values retrieved by yarn view.
93
+ *
94
+ * @param packageName Name of the package
95
+ * @param fields Array of fields like versions, time, version
96
+ * @param currentVersion
97
+ * @returns Promised result
98
+ */
99
+ function viewMany(packageName, fields, currentVersion, { timeout } = {}) {
100
+ if (currentVersion && (!semver.validRange(currentVersion) ||
101
+ versionUtil.isWildCard(currentVersion))) {
102
+ return Promise.resolve({})
103
+ }
104
+
105
+ yarnConfig.fullMetadata = fields.includes('time')
106
+
107
+ return pacote.packument(packageName, { ...yarnConfig, timeout })
108
+ .then(result =>
109
+ fields.reduce((accum, field) => ({
110
+ ...accum,
111
+ [field]: field.startsWith('dist-tags.') && result.versions ?
112
+ result.versions[_.get(result, field)] :
113
+ result[field],
114
+ }), {})
115
+ )
116
+ }
117
+
118
+ /**
119
+ * @param versions Array of all available versions
120
+ * @param pre Enabled prerelease?
121
+ * @returns An array of versions with the release versions filtered out
122
+ */
123
+ function filterOutPrereleaseVersions(versions, pre) {
124
+ return pre ? versions : versions.filter(
125
+ version => !versionUtil.isPre(version))
126
+ }
127
+
128
+ /**
129
+ * @param versions Object with all versions
130
+ * @param enginesNode Package engines.node range
131
+ * @returns An array of versions which satisfies engines.node range
132
+ */
133
+ function doesSatisfyEnginesNode(versions, enginesNode) {
134
+ if (!versions) {
135
+ return []
136
+ }
137
+ if (!enginesNode) {
138
+ return Object.keys(versions)
139
+ }
140
+ const minVersion = _.get(semver.minVersion(enginesNode), 'version')
141
+ if (!minVersion) {
142
+ return Object.keys(versions)
143
+ }
144
+ return Object.keys(versions).filter(version => {
145
+ const versionEnginesNode = _.get(versions[version], 'engines.node')
146
+ return versionEnginesNode &&
147
+ semver.satisfies(minVersion, versionEnginesNode)
148
+ })
149
+ }
150
+
151
+ /**
152
+ * Spawn yarn requires a different command on Windows.
153
+ *
154
+ * @param args
155
+ * @param [yarnOptions={}]
156
+ * @param [spawnOptions={}]
157
+ * @returns
158
+ */
159
+ function spawnYarn(args, yarnOptions = {}, spawnOptions = {}) {
160
+ const cmd = process.platform === 'win32' ? 'yarn.cmd' : 'yarn'
161
+
162
+ const fullArgs = [].concat(
163
+ yarnOptions.global ? 'global' : [],
164
+ args,
165
+ '--depth=0',
166
+ yarnOptions.prefix ? `--prefix=${yarnOptions.prefix}` : [],
167
+ '--json',
168
+ '--no-progress'
169
+ )
170
+ return spawn(cmd, fullArgs, spawnOptions)
171
+ }
172
+
173
+ /**
174
+ * Get platform-specific default prefix to pass on to yarn.
175
+ *
176
+ * @param options
177
+ * @param [options.global]
178
+ * @param [options.prefix]
179
+ * @returns
180
+ */
181
+ function defaultPrefix(options) {
182
+
183
+ if (options.prefix) {
184
+ return Promise.resolve(options.prefix)
185
+ }
186
+
187
+ const cmd = process.platform === 'win32' ? 'yarn.cmd' : 'yarn'
188
+
189
+ return spawn(cmd, ['global', 'dir']).then(prefix => {
190
+ // FIX: for ncu -g doesn't work on homebrew or windows #146
191
+ // https://github.com/raineorshine/npm-check-updates/issues/146
192
+
193
+ return options.global && prefix.match('Cellar') ? '/usr/local' :
194
+
195
+ // Workaround: get prefix on windows for global packages
196
+ // Only needed when using npm api directly
197
+ process.platform === 'win32' && options.global && !process.env.prefix ?
198
+ prefix ? prefix.trim() : `${process.env.LOCALAPPDATA}\\Yarn\\Data\\global` :
199
+ null
200
+ })
201
+ }
202
+
203
+ module.exports = {
204
+
205
+ /**
206
+ * @param [options]
207
+ * @param [options.cwd]
208
+ * @param [options.global]
209
+ * @param [options.prefix]
210
+ * @returns
211
+ */
212
+ list(options = {}) {
213
+
214
+ return spawnYarn('list', options, options.cwd ? { cwd: options.cwd } : {}).then(async jsonLines => {
215
+ const json = await parseJsonLines(jsonLines)
216
+
217
+ return cint.mapObject(json.dependencies, (name, info) => ({
218
+ // unmet peer dependencies have a different structure
219
+ [name]: info.version || (info.required && info.required.version),
220
+ }))
221
+ })
222
+ .catch(async jsonLines => {
223
+ await parseJsonLines(jsonLines)
224
+ return {}
225
+ })
226
+ },
227
+
228
+ /**
229
+ * @param packageName
230
+ * @param currentVersion
231
+ * @param options
232
+ * @returns
233
+ */
234
+ latest(packageName, currentVersion, options = {}) {
235
+ return viewOne(packageName, 'dist-tags.latest', currentVersion,
236
+ { timeout: options.timeout }).then(latest => {
237
+ // if latest exists and latest is not a prerelease version, return it
238
+ // if latest exists and latest is a prerelease version and --pre is specified, return it
239
+ // if latest exists and latest not satisfies min version of engines.node
240
+ if (latest && (!versionUtil.isPre(latest.version) || options.pre) &&
241
+ doesSatisfyEnginesNode({ [latest.version]: latest },
242
+ options.enginesNode).length) {
243
+ return latest.version
244
+ // if latest is a prerelease version and --pre is not specified, find the next
245
+ // version that is not a prerelease
246
+ }
247
+ else {
248
+ return viewOne(packageName, 'versions', currentVersion)
249
+ .then(versions => {
250
+ versions = doesSatisfyEnginesNode(versions, options.enginesNode)
251
+ return _.last(filterOutPrereleaseVersions(versions, options.pre))
252
+ })
253
+ }
254
+ })
255
+ },
256
+
257
+ /**
258
+ * @param packageName
259
+ * @param currentVersion
260
+ * @param options
261
+ * @returns
262
+ */
263
+ newest(packageName, currentVersion, options = {}) {
264
+ return viewMany(packageName, ['time', 'versions'], currentVersion,
265
+ { timeout: options.timeout }).then(result => {
266
+ const versions = doesSatisfyEnginesNode(result.versions,
267
+ options.enginesNode)
268
+ return Object.keys(result.time || {}).reduce((accum, key) =>
269
+ accum.concat(
270
+ TIME_FIELDS.includes(key) || versions.includes(key) ? key : []), []
271
+ )
272
+ }).then(_.partialRight(_.pullAll, TIME_FIELDS)).then(versions =>
273
+ _.last(filterOutPrereleaseVersions(versions,
274
+ options.pre == null || options.pre))
275
+ )
276
+ },
277
+
278
+ /**
279
+ * @param packageName
280
+ * @param currentVersion
281
+ * @param options
282
+ * @returns
283
+ */
284
+ greatest(packageName, currentVersion, options = {}) {
285
+ return viewOne(packageName, 'versions', currentVersion,
286
+ { timeout: options.timeout }).then(versions =>
287
+ // eslint-disable-next-line fp/no-mutating-methods
288
+ _.last(filterOutPrereleaseVersions(
289
+ doesSatisfyEnginesNode(versions, options.enginesNode),
290
+ options.pre == null || options.pre).sort(versionUtil.compareVersions)
291
+ )
292
+ )
293
+ },
294
+
295
+ /**
296
+ * @param packageName
297
+ * @param currentVersion
298
+ * @param options
299
+ * @returns
300
+ */
301
+ greatestMajor(packageName, currentVersion, options = {}) {
302
+ return viewOne(packageName, 'versions', currentVersion,
303
+ { timeout: options.timeout }).then(versions =>
304
+ versionUtil.findGreatestByLevel(
305
+ filterOutPrereleaseVersions(
306
+ doesSatisfyEnginesNode(versions, options.enginesNode),
307
+ options.pre
308
+ ),
309
+ currentVersion,
310
+ 'major'
311
+ )
312
+ )
313
+ },
314
+
315
+ /**
316
+ * @param packageName
317
+ * @param currentVersion
318
+ * @param options
319
+ * @returns
320
+ */
321
+ greatestMinor(packageName, currentVersion, options) {
322
+ return viewOne(packageName, 'versions', currentVersion,
323
+ { timeout: options.timeout }).then(versions =>
324
+ versionUtil.findGreatestByLevel(
325
+ filterOutPrereleaseVersions(
326
+ doesSatisfyEnginesNode(versions, options.enginesNode),
327
+ options.pre
328
+ ),
329
+ currentVersion,
330
+ 'minor'
331
+ )
332
+ )
333
+ },
334
+
335
+ defaultPrefix,
336
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-check-updates",
3
- "version": "7.0.4-alpha.2",
3
+ "version": "7.1.1",
4
4
  "author": "Tomas Junnonen <tomas1@gmail.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [
@@ -31,7 +31,7 @@
31
31
  "lint": "eslint --cache --cache-location node_modules/.cache/.eslintcache --report-unused-disable-directives .",
32
32
  "lintfix": "npm run lint -- --fix",
33
33
  "watch": "chokidar \"lib/**/*.js\" -c \"npm run test\"",
34
- "mocha": "mocha && mocha test/individual",
34
+ "mocha": "mocha test test/package-managers/npm test/package-managers/yarn",
35
35
  "test": "npm run lint && npm run mocha"
36
36
  },
37
37
  "bin": {
@@ -54,6 +54,7 @@
54
54
  "find-up": "4.1.0",
55
55
  "get-stdin": "^8.0.0",
56
56
  "json-parse-helpfulerror": "^1.0.3",
57
+ "jsonlines": "^0.1.1",
57
58
  "libnpmconfig": "^1.2.1",
58
59
  "lodash": "^4.17.19",
59
60
  "p-map": "^4.0.0",
@@ -82,7 +83,8 @@
82
83
  "mocha": "^8.1.1",
83
84
  "nyc": "^15.1.0",
84
85
  "should": "^13.2.3",
85
- "tmp": "0.2.1"
86
+ "tmp": "0.2.1",
87
+ "yarn": "^1.22.4"
86
88
  },
87
89
  "files": [
88
90
  "bin",