update-browserslist-db 1.3.2 → 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.
- package/index.js +277 -13
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
let { execSync } = require('child_process')
|
|
1
|
+
let { execFileSync, execSync } = require('child_process')
|
|
2
2
|
let escalade = require('escalade/sync')
|
|
3
|
-
let { existsSync, readFileSync, writeFileSync } = require('fs')
|
|
3
|
+
let { existsSync, readFileSync, unlinkSync, writeFileSync } = require('fs')
|
|
4
4
|
let { dirname, join } = require('path')
|
|
5
5
|
let pico = require('picocolors')
|
|
6
6
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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 = {}
|
|
@@ -224,7 +255,7 @@ function updatePackageManually(print, lock, latest) {
|
|
|
224
255
|
let caniuseVersions = Object.keys(lockfileData.versions).sort()
|
|
225
256
|
if (caniuseVersions.length === 1 && caniuseVersions[0] === latest.version) {
|
|
226
257
|
print(
|
|
227
|
-
'Installed version:
|
|
258
|
+
'Installed version: ' +
|
|
228
259
|
pico.bold(pico.green(caniuseVersions[0])) +
|
|
229
260
|
'\n' +
|
|
230
261
|
pico.bold(pico.green('caniuse-lite is up to date')) +
|
|
@@ -238,7 +269,7 @@ function updatePackageManually(print, lock, latest) {
|
|
|
238
269
|
}
|
|
239
270
|
print(
|
|
240
271
|
'Installed version' +
|
|
241
|
-
(caniuseVersions.length === 1 ? ':
|
|
272
|
+
(caniuseVersions.length === 1 ? ': ' : 's: ') +
|
|
242
273
|
pico.bold(pico.red(caniuseVersions.join(', '))) +
|
|
243
274
|
'\n' +
|
|
244
275
|
'Removing old caniuse-lite from lock file\n'
|
|
@@ -328,6 +359,241 @@ function updateBun(print, lock, latest) {
|
|
|
328
359
|
updateWith(print, 'bun install', 'Removing the temporary override')
|
|
329
360
|
}
|
|
330
361
|
|
|
362
|
+
function commandErrorText(error) {
|
|
363
|
+
return [error.message, error.stdout, error.stderr]
|
|
364
|
+
.filter(Boolean)
|
|
365
|
+
.map(i => i.toString())
|
|
366
|
+
.join('\n')
|
|
367
|
+
}
|
|
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
|
+
|
|
397
|
+
function getPnpmConfig(name) {
|
|
398
|
+
let output = runPnpm([
|
|
399
|
+
'config',
|
|
400
|
+
'get',
|
|
401
|
+
name,
|
|
402
|
+
'--json'
|
|
403
|
+
]).toString()
|
|
404
|
+
return output.trim() === 'undefined' ? undefined : JSON.parse(output)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function getPnpmPackageTimes(name) {
|
|
408
|
+
let times
|
|
409
|
+
try {
|
|
410
|
+
times = JSON.parse(
|
|
411
|
+
runPnpm(['info', name, 'time', '--json']).toString()
|
|
412
|
+
)
|
|
413
|
+
} catch (error) /* c8 ignore start */ {
|
|
414
|
+
throw new BrowserslistUpdateError(
|
|
415
|
+
'Cannot read publication dates of ' +
|
|
416
|
+
name +
|
|
417
|
+
' from pnpm. Refusing to bypass pnpm minimumReleaseAge.\n' +
|
|
418
|
+
commandErrorText(error)
|
|
419
|
+
)
|
|
420
|
+
} /* c8 ignore end */
|
|
421
|
+
if (!times || typeof times !== 'object' || Array.isArray(times)) {
|
|
422
|
+
/* c8 ignore next 6 */
|
|
423
|
+
throw new BrowserslistUpdateError(
|
|
424
|
+
'pnpm returned no publication dates for ' +
|
|
425
|
+
name +
|
|
426
|
+
'. Refusing to bypass pnpm minimumReleaseAge.'
|
|
427
|
+
)
|
|
428
|
+
}
|
|
429
|
+
return times
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function getMatureVersion(name, minimumReleaseAge) {
|
|
433
|
+
let cutoff = Date.now() - minimumReleaseAge * 60 * 1000
|
|
434
|
+
let times = getPnpmPackageTimes(name)
|
|
435
|
+
|
|
436
|
+
let mature, matureTime
|
|
437
|
+
for (let version of Object.keys(times)) {
|
|
438
|
+
let time = Date.parse(times[version])
|
|
439
|
+
if (!/^\d+\.\d+\.\d+$/.test(version) || !(time <= cutoff)) continue
|
|
440
|
+
if (matureTime === undefined || time > matureTime) {
|
|
441
|
+
mature = version
|
|
442
|
+
matureTime = time
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (!mature) {
|
|
447
|
+
throw new BrowserslistUpdateError(
|
|
448
|
+
'Cannot find a ' +
|
|
449
|
+
name +
|
|
450
|
+
' version old enough for pnpm minimumReleaseAge (' +
|
|
451
|
+
minimumReleaseAge +
|
|
452
|
+
' minutes). Wait for a release to pass the age gate and try again.'
|
|
453
|
+
)
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return mature
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function restoreFile(file, original) {
|
|
460
|
+
if (original === undefined) {
|
|
461
|
+
if (existsSync(file)) unlinkSync(file)
|
|
462
|
+
} else {
|
|
463
|
+
writeFileSync(file, original)
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function updatePnpmStrict(print, lock, packages) {
|
|
468
|
+
let minimumReleaseAge = getPnpmConfig('minimumReleaseAge')
|
|
469
|
+
if (
|
|
470
|
+
typeof minimumReleaseAge !== 'number' ||
|
|
471
|
+
!Number.isFinite(minimumReleaseAge) ||
|
|
472
|
+
minimumReleaseAge <= 0
|
|
473
|
+
) {
|
|
474
|
+
throw new BrowserslistUpdateError(
|
|
475
|
+
'Cannot read a valid pnpm minimumReleaseAge. Refusing to disable pnpm security checks.'
|
|
476
|
+
)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
let versions = {}
|
|
480
|
+
for (let name of packages) {
|
|
481
|
+
versions[name] = getMatureVersion(name, minimumReleaseAge)
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
let originalOverrides = getPnpmConfig('overrides')
|
|
485
|
+
if (
|
|
486
|
+
originalOverrides === undefined ||
|
|
487
|
+
originalOverrides === null ||
|
|
488
|
+
Array.isArray(originalOverrides) ||
|
|
489
|
+
typeof originalOverrides !== 'object'
|
|
490
|
+
) {
|
|
491
|
+
originalOverrides = {}
|
|
492
|
+
}
|
|
493
|
+
let temporaryOverrides = { ...originalOverrides, ...versions }
|
|
494
|
+
let packageDir = dirname(lock.file)
|
|
495
|
+
let workspaceFile = join(packageDir, 'pnpm-workspace.yaml')
|
|
496
|
+
let originalWorkspace = existsSync(workspaceFile)
|
|
497
|
+
? readFileSync(workspaceFile)
|
|
498
|
+
: undefined
|
|
499
|
+
let originalLockfile = readFileSync(lock.file)
|
|
500
|
+
let packageFile = join(packageDir, 'package.json')
|
|
501
|
+
let originalPackage = existsSync(packageFile)
|
|
502
|
+
? readFileSync(packageFile)
|
|
503
|
+
: undefined
|
|
504
|
+
let command = 'pnpm config set overrides'
|
|
505
|
+
|
|
506
|
+
print(
|
|
507
|
+
'Strict pnpm minimumReleaseAge detected\n' +
|
|
508
|
+
'Latest policy-compliant: ' +
|
|
509
|
+
pico.bold(pico.green(versions['caniuse-lite'])) +
|
|
510
|
+
'\n' +
|
|
511
|
+
Object.keys(versions)
|
|
512
|
+
.filter(name => name !== 'caniuse-lite')
|
|
513
|
+
.map(name => ' ' + name + ': ' + versions[name] + '\n')
|
|
514
|
+
.join('')
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
try {
|
|
518
|
+
runPnpm(
|
|
519
|
+
[
|
|
520
|
+
'config',
|
|
521
|
+
'set',
|
|
522
|
+
'overrides',
|
|
523
|
+
JSON.stringify(temporaryOverrides),
|
|
524
|
+
'--json',
|
|
525
|
+
'--location',
|
|
526
|
+
'project'
|
|
527
|
+
],
|
|
528
|
+
{ cwd: packageDir }
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
command = 'pnpm install --lockfile-only'
|
|
532
|
+
print(
|
|
533
|
+
'Updating caniuse-lite version\n' +
|
|
534
|
+
pico.yellow('$ ' + command) +
|
|
535
|
+
' (with temporary policy-compliant overrides)\n'
|
|
536
|
+
)
|
|
537
|
+
runPnpm(['install', '--lockfile-only'], { cwd: packageDir })
|
|
538
|
+
|
|
539
|
+
restoreFile(workspaceFile, originalWorkspace)
|
|
540
|
+
// The lockfile no longer matches the restored overrides, and pnpm
|
|
541
|
+
// freezes the lockfile by default in CI
|
|
542
|
+
command = 'pnpm install --no-frozen-lockfile'
|
|
543
|
+
print(
|
|
544
|
+
'Removing temporary pnpm overrides\n' + pico.yellow('$ ' + command) + '\n'
|
|
545
|
+
)
|
|
546
|
+
runPnpm(['install', '--no-frozen-lockfile'], {
|
|
547
|
+
cwd: packageDir
|
|
548
|
+
})
|
|
549
|
+
} catch (error) /* c8 ignore start */ {
|
|
550
|
+
restoreFile(workspaceFile, originalWorkspace)
|
|
551
|
+
restoreFile(packageFile, originalPackage)
|
|
552
|
+
writeFileSync(lock.file, originalLockfile)
|
|
553
|
+
print(pico.red(commandErrorText(error)))
|
|
554
|
+
print(
|
|
555
|
+
pico.red('\n\nProblem with `' + command + '` call. Run it manually.\n')
|
|
556
|
+
)
|
|
557
|
+
process.exit(1)
|
|
558
|
+
} /* c8 ignore end */
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function updatePnpm(print, lock) {
|
|
562
|
+
let lockContent = readFileSync(lock.file).toString()
|
|
563
|
+
let packages = lockContent.includes('baseline-browser-mapping')
|
|
564
|
+
? ['caniuse-lite', 'baseline-browser-mapping']
|
|
565
|
+
: ['caniuse-lite']
|
|
566
|
+
let command = 'pnpm up --depth=9999 --no-save ' + packages.join(' ')
|
|
567
|
+
|
|
568
|
+
print('Updating caniuse-lite version\n' + pico.yellow('$ ' + command) + '\n')
|
|
569
|
+
try {
|
|
570
|
+
runPnpm(['up', '--depth=9999', '--no-save', ...packages], {
|
|
571
|
+
stdio: 'pipe'
|
|
572
|
+
})
|
|
573
|
+
} catch (error) /* c8 ignore start */ {
|
|
574
|
+
let text = commandErrorText(error)
|
|
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
|
+
) {
|
|
586
|
+
updatePnpmStrict(print, lock, packages)
|
|
587
|
+
} else {
|
|
588
|
+
print(pico.red(text))
|
|
589
|
+
print(
|
|
590
|
+
pico.red('\n\nProblem with `' + command + '` call. Run it manually.\n')
|
|
591
|
+
)
|
|
592
|
+
process.exit(1)
|
|
593
|
+
}
|
|
594
|
+
} /* c8 ignore end */
|
|
595
|
+
}
|
|
596
|
+
|
|
331
597
|
function updateWith(print, cmd, message = 'Updating caniuse-lite version') {
|
|
332
598
|
print(message + '\n' + pico.yellow('$ ' + cmd) + '\n')
|
|
333
599
|
try {
|
|
@@ -361,16 +627,14 @@ module.exports = function updateDB(print = defaultPrint) {
|
|
|
361
627
|
listError = e
|
|
362
628
|
}
|
|
363
629
|
|
|
364
|
-
print(
|
|
630
|
+
print(
|
|
631
|
+
'Registry latest: ' + pico.bold(pico.green(latest.version)) + '\n'
|
|
632
|
+
)
|
|
365
633
|
|
|
366
634
|
if (lock.mode === 'yarn' && lock.version !== 1) {
|
|
367
635
|
updateWith(print, YARN_CMD + ' up -R caniuse-lite baseline-browser-mapping')
|
|
368
636
|
} else if (lock.mode === 'pnpm') {
|
|
369
|
-
|
|
370
|
-
let packages = lockContent.includes('baseline-browser-mapping')
|
|
371
|
-
? 'caniuse-lite baseline-browser-mapping'
|
|
372
|
-
: 'caniuse-lite'
|
|
373
|
-
updateWith(print, 'pnpm up --depth=9999 --no-save ' + packages)
|
|
637
|
+
updatePnpm(print, lock)
|
|
374
638
|
} else if (lock.mode === 'bun') {
|
|
375
639
|
updateBun(print, lock, latest)
|
|
376
640
|
} else if (lock.mode === 'deno') {
|