update-browserslist-db 1.3.3 → 1.3.4

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.
Files changed (2) hide show
  1. package/index.js +84 -11
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -17,7 +17,6 @@ function BrowserslistUpdateError(message) {
17
17
 
18
18
  BrowserslistUpdateError.prototype = Error.prototype
19
19
 
20
- // Check if HADOOP_HOME is set to determine if this is running in a Hadoop environment
21
20
  const YARN_CMD = process.env.HADOOP_HOME ? 'yarnpkg' : 'yarn'
22
21
 
23
22
  /* c8 ignore next 3 */
@@ -70,6 +69,14 @@ function getLatestInfo(lock) {
70
69
  try {
71
70
  if (lock.mode === 'yarn') {
72
71
  if (lock.version === 1) {
72
+ let yarnMajorVersion = getYarnMajor()
73
+ if (yarnMajorVersion !== 1) {
74
+ throw new BrowserslistUpdateError(
75
+ 'This project has a Yarn v1 (classic) lockfile, but the installed ' +
76
+ `Yarn is Berry v${yarnMajorVersion}. Install and activate Yarn classic ` +
77
+ 'to update caniuse-lite.'
78
+ )
79
+ }
73
80
  return JSON.parse(
74
81
  execSync(YARN_CMD + ' info caniuse-lite --json').toString()
75
82
  ).data
@@ -96,7 +103,20 @@ function getLatestInfo(lock) {
96
103
 
97
104
  return JSON.parse(execSync('npm show caniuse-lite --json').toString())
98
105
  } catch (e) {
99
- if (e.code === 'ENOENT' || e.status === 127) {
106
+ // ENOENT / status 127 are the POSIX "command not found" signals. cmd.exe
107
+ // instead reports `'<binary>' is not recognized as an internal or external
108
+ // command`. Anchor that phrase to the binary we actually launched so an
109
+ // unrelated "is not recognized" line in a real failure's output (e.g. a
110
+ // nested tool) is not mistaken for a missing package manager.
111
+ let binary = lock.mode === 'yarn' ? YARN_CMD : lock.mode
112
+ let missingOnWindows = new RegExp(
113
+ "'" + binary + "' is not recognized as an internal or external command"
114
+ )
115
+ if (
116
+ e.code === 'ENOENT' ||
117
+ e.status === 127 ||
118
+ missingOnWindows.test(commandErrorText(e))
119
+ ) {
100
120
  throw new BrowserslistUpdateError(
101
121
  'Cannot find ' +
102
122
  lock.mode +
@@ -109,6 +129,11 @@ function getLatestInfo(lock) {
109
129
  }
110
130
  }
111
131
 
132
+ function getYarnMajor() {
133
+ let version = execSync(YARN_CMD + ' --version').toString().trim()
134
+ return Number(version.split('.')[0])
135
+ }
136
+
112
137
  function getBrowsers() {
113
138
  let browserslist = require('browserslist')
114
139
  return browserslist().reduce((result, entry) => {
@@ -171,7 +196,13 @@ function deletePackage(node, metadata) {
171
196
  let yarnVersionRe = /version "(.*?)"/
172
197
 
173
198
  function updateYarnLockfile(lock, latest) {
174
- let blocks = lock.content.split(/(\n{2,})/).map(block => {
199
+ // Split entries on blank lines using LF. A CRLF lockfile has `\r\n\r\n`
200
+ // between entries, which `\n{2,}` cannot match, so the whole file would
201
+ // collapse into one block and no version be detected. CRLF can occur on any
202
+ // OS (git autocrlf, editor settings), so normalize regardless of platform;
203
+ // updateLockfile restores the original EOL on the returned content.
204
+ let normalized = lock.content.replace(/\r\n/g, '\n')
205
+ let blocks = normalized.split(/(\n{2,})/).map(block => {
175
206
  return block.split('\n')
176
207
  })
177
208
  let versions = {}
@@ -335,8 +366,36 @@ function commandErrorText(error) {
335
366
  .join('\n')
336
367
  }
337
368
 
369
+ function quotePnpmArg(arg) {
370
+ // cmd.exe expands %VAR% and !VAR! even inside double quotes. It also ignores
371
+ // the backslash in `\"`, so JSON values end up outside its quotes, where
372
+ // ^ & | < > ( ) run commands or redirect output. Refuse rather than run
373
+ // a mangled command.
374
+ if (/[%!^&|<>()]/.test(arg)) {
375
+ throw new BrowserslistUpdateError(
376
+ 'Cannot safely pass any of %!^&|<>() to pnpm on Windows: ' + arg
377
+ )
378
+ }
379
+ if (!/[\s"]/.test(arg)) return arg
380
+ // Escape embedded quotes (doubling any backslash run that precedes them) and
381
+ // double a trailing backslash run so it cannot escape the closing quote.
382
+ return '"' + arg.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, '$1$1') + '"'
383
+ }
384
+
385
+ function runPnpm(args, options = {}) {
386
+ // Node on Windows cannot launch pnpm's `.cmd` shim without a shell
387
+ if (process.platform === 'win32') {
388
+ return execFileSync('pnpm', args.map(quotePnpmArg), {
389
+ ...options,
390
+ shell: true
391
+ })
392
+ }
393
+
394
+ return execFileSync('pnpm', args, options)
395
+ }
396
+
338
397
  function getPnpmConfig(name) {
339
- let output = execFileSync('pnpm', [
398
+ let output = runPnpm([
340
399
  'config',
341
400
  'get',
342
401
  name,
@@ -349,7 +408,7 @@ function getPnpmPackageTimes(name) {
349
408
  let times
350
409
  try {
351
410
  times = JSON.parse(
352
- execFileSync('pnpm', ['info', name, 'time', '--json']).toString()
411
+ runPnpm(['info', name, 'time', '--json']).toString()
353
412
  )
354
413
  } catch (error) /* c8 ignore start */ {
355
414
  throw new BrowserslistUpdateError(
@@ -438,6 +497,10 @@ function updatePnpmStrict(print, lock, packages) {
438
497
  ? readFileSync(workspaceFile)
439
498
  : undefined
440
499
  let originalLockfile = readFileSync(lock.file)
500
+ let packageFile = join(packageDir, 'package.json')
501
+ let originalPackage = existsSync(packageFile)
502
+ ? readFileSync(packageFile)
503
+ : undefined
441
504
  let command = 'pnpm config set overrides'
442
505
 
443
506
  print(
@@ -452,8 +515,7 @@ function updatePnpmStrict(print, lock, packages) {
452
515
  )
453
516
 
454
517
  try {
455
- execFileSync(
456
- 'pnpm',
518
+ runPnpm(
457
519
  [
458
520
  'config',
459
521
  'set',
@@ -472,7 +534,7 @@ function updatePnpmStrict(print, lock, packages) {
472
534
  pico.yellow('$ ' + command) +
473
535
  ' (with temporary policy-compliant overrides)\n'
474
536
  )
475
- execFileSync('pnpm', ['install', '--lockfile-only'], { cwd: packageDir })
537
+ runPnpm(['install', '--lockfile-only'], { cwd: packageDir })
476
538
 
477
539
  restoreFile(workspaceFile, originalWorkspace)
478
540
  // The lockfile no longer matches the restored overrides, and pnpm
@@ -481,11 +543,12 @@ function updatePnpmStrict(print, lock, packages) {
481
543
  print(
482
544
  'Removing temporary pnpm overrides\n' + pico.yellow('$ ' + command) + '\n'
483
545
  )
484
- execFileSync('pnpm', ['install', '--no-frozen-lockfile'], {
546
+ runPnpm(['install', '--no-frozen-lockfile'], {
485
547
  cwd: packageDir
486
548
  })
487
549
  } catch (error) /* c8 ignore start */ {
488
550
  restoreFile(workspaceFile, originalWorkspace)
551
+ restoreFile(packageFile, originalPackage)
489
552
  writeFileSync(lock.file, originalLockfile)
490
553
  print(pico.red(commandErrorText(error)))
491
554
  print(
@@ -504,12 +567,22 @@ function updatePnpm(print, lock) {
504
567
 
505
568
  print('Updating caniuse-lite version\n' + pico.yellow('$ ' + command) + '\n')
506
569
  try {
507
- execFileSync('pnpm', ['up', '--depth=9999', '--no-save', ...packages], {
570
+ runPnpm(['up', '--depth=9999', '--no-save', ...packages], {
508
571
  stdio: 'pipe'
509
572
  })
510
573
  } catch (error) /* c8 ignore start */ {
511
574
  let text = commandErrorText(error)
512
- if (text.includes('ERR_PNPM_STRICT_MIN_RELEASE_AGE_REQUIRES_SAVE')) {
575
+ // pnpm versions before 12.4.2 can fail `up --no-save` under strict
576
+ // minimumReleaseAge. The error differs by version: 10 uses
577
+ // NO_MATURE_MATCHING_VERSION, 11 uses MINIMUM_RELEASE_AGE_VIOLATION, and
578
+ // 12.0–12.4.1 uses STRICT_MIN_RELEASE_AGE_REQUIRES_SAVE. In each case,
579
+ // resolve and pin the newest mature versions ourselves. pnpm 12.4.2+ does
580
+ // this natively and succeeds before reaching this catch.
581
+ if (
582
+ text.includes('ERR_PNPM_STRICT_MIN_RELEASE_AGE_REQUIRES_SAVE') ||
583
+ text.includes('ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION') ||
584
+ text.includes('ERR_PNPM_NO_MATURE_MATCHING_VERSION')
585
+ ) {
513
586
  updatePnpmStrict(print, lock, packages)
514
587
  } else {
515
588
  print(pico.red(text))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "update-browserslist-db",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "CLI tool to update caniuse-lite to refresh target browsers from Browserslist config",
5
5
  "keywords": [
6
6
  "browsers",