'use strict'; var path = require('path'); var picomatch = require('picomatch'); var promises = require('fs/promises'); var child_process = require('child_process'); var url = require('url'); var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; function log$1(message) { console.log('dep-dev', message); } let cwd$1; function shorten(path$1) { if (!cwd$1) cwd$1 = process.cwd(); return path.relative(cwd$1, path$1) } async function readJSON(config, verbose) { if (verbose) log$1(`reading "${shorten(config)}"`); const content = await promises.readFile(config, 'utf8'); return JSON.parse(content) } async function findRoot(curDir, verbose) { const startDir = curDir || process.env.INIT_CWD || process.cwd(); let dir = path.resolve(startDir), prevDir, path$1; do { try { if (verbose) log$1(`looking for package.json in "${shorten(dir)}"`); path$1 = path.join(dir, 'package.json'); await promises.access(path$1); if (verbose) log$1(`"${shorten(path$1)}" found`); return dir } catch (err) { if (err.code !== 'ENOENT') throw err if (verbose) log$1(`"${shorten(path$1)}" not found`); } prevDir = dir; dir = path.resolve(dir, '..'); } while (prevDir !== dir) throw new Error(`package.json not found in ${startDir} and its ancestors`) } async function loadConfig(type, root, verbose) { const config = path.join(root, `package-${type}.json`); try { return await readJSON(config, verbose) } catch (err) { if (err.code !== 'ENOENT') throw err if (verbose) log$1(`"${shorten(config)}" not found`); return await readJSON(path.join(root, 'package.json'), verbose) } } function listDeps(deps, links) { console.log(); if (Array.isArray(deps)) for (const dep of deps) console.log(`+ ${dep}`); else for (const name in deps) console.log(`+ ${name}${links ? ' => ' : '@'}${deps[name]}`); } function spawnProcess(command, args, { cwd } = {}) { return new Promise((resolve, reject) => { const child = child_process.spawn(command, args, { cwd, shell: true }) .on('error', reject) .on('exit', code => code ? reject(new Error(`${command} exited with ${code}`)) : resolve()); child.stdout.on('data', data => process.stdout.write(data.toString())); child.stderr.on('data', data => process.stderr.write(data.toString())); }); } async function resolveDeps(deps, root, strict, verbose) { const pkgs = await Promise.all(deps.map(async dep => { const version = dep.indexOf('@', 1); const name = version > 0 ? dep.slice(0, version) : dep; const pkg = path.join(root, 'node_modules/', name, 'package.json'); try { return await readJSON(pkg, verbose) } catch (err) { if (err.code !== 'ENOENT') throw err if (verbose) log$1(`"${shorten(pkg)}" not found`); if (strict) throw err return { name } } })); deps = {}; for (const { name, version } of pkgs) deps[name] = version; return deps } async function resolveConfig(type, config, root, preferSeparate, checkProperty, verbose) { let pkg; if (config === undefined) { config = path.join(root, `package-${type}.json`); try { pkg = await readJSON(config, verbose); } catch (err) { if (err.code !== 'ENOENT') throw err if (verbose) log$1(`"${shorten(config)}" not found`); const pkgConfig = path.join(root, 'package.json'); pkg = await readJSON(pkgConfig, verbose); if (preferSeparate && !pkg[checkProperty]) { pkg = {}; } else { config = pkgConfig; } } } else if (config === true) { try { config = path.join(root, `package-${type}.json`); pkg = await readJSON(config, verbose); } catch (err) { if (err.code !== 'ENOENT') throw err if (verbose) log$1(`"${shorten(config)}" not found`); pkg = {}; } } else if (config) { try { pkg = await readJSON(config, verbose); } catch (err) { if (err.code !== 'ENOENT') throw err if (verbose) log$1(`"${shorten(config)}" not found`); pkg = {}; } } else { config = path.join(root, 'package.json'); pkg = await readJSON(config, verbose); } if (verbose) log$1(`"choosing "${shorten(config)}"`); return { config, pkg } } async function writeJSON(config, pkg, lineBreak, verbose) { if (verbose) log$1(`writing "${shorten(config)}"`); let contents = JSON.stringify(pkg, undefined, 2); if (lineBreak !== false) contents = `${contents}\n`; await promises.writeFile(config, contents); } async function upgradeDeps(deps, config, root, lineBreak, verbose) { let pkg; ({ config, pkg } = await resolveConfig('updates', config, root, undefined, undefined, verbose)); let { updateDependencies } = pkg; if (!updateDependencies) updateDependencies = []; const newDeps = deps.filter(dep => !updateDependencies.includes(dep)); pkg.updateDependencies = updateDependencies.push(...newDeps); await writeJSON(config, pkg, lineBreak, verbose); } function isPattern(pattern) { return pattern.includes('*') || pattern.includes('?') } function log(message) { console.log('dep-update', message); } async function deepDeps(root, pending, verbose, visited = new Set()) { await Promise.all(pending.map(async dep => { const pkg = `${root}/node_modules/${dep}/package.json`; let { peerDependencies = {} } = await readJSON(pkg, verbose); peerDependencies = Object.keys(peerDependencies); if (peerDependencies.length) { if (verbose) log(`peer dependencies found: ${peerDependencies.join(', ')}`); for (const dep of peerDependencies) visited.add(dep); await deepDeps(root, peerDependencies, verbose, visited); } })); return visited } async function upgradeDependencies(newDeps, { config, cwd, deep, save, lineBreak, progress, list, verbose, dryRun } = {}) { const start = performance.now(); if (verbose === undefined) verbose = process.env.npm_config_loglevel === 'verbose'; let deps = newDeps, root; if (!(deps && deps.length)) { if (config) { if (config === true) { root = await findRoot(cwd, verbose); config = path.join(root, 'package-updates.json'); } else { config = path.resolve(config); } deps = await readJSON(config, verbose); } else { root = await findRoot(cwd, verbose); deps = await loadConfig('updates', root, verbose); } deps = deps.updateDependencies; } if (!(deps && deps.length)) { return console.log('no dependencies to update') } if (!root) root = await findRoot(cwd, verbose); let { dependencies = {}, devDependencies = {} } = await readJSON(path.join(root, 'package.json'), verbose); dependencies = Object.keys(dependencies); const peers = deep && deps.some(isPattern) ? await deepDeps(root, dependencies, verbose) : []; dependencies = new Set([...dependencies, ...peers, ...Object.keys(devDependencies)]); deps = deps.reduce((result, pattern) => { if (isPattern(pattern)) { if (verbose) log(`looking for dependencies matching ${pattern}`); const match = picomatch(pattern); let updated; for (const dep of dependencies) { if (match(dep)) { if (verbose) log(`dependency ${dep} matched`); result.push(dep); updated = true; } } if (!updated) throw new Error(`no dependency matched ${pattern}`) } else { if (verbose) log(`dependency ${pattern} included`); result.push(pattern); } return result }, []); const args = ['up', '--no-save', '--no-audit', '--no-update-notifier']; if (progress === undefined) progress = !('npm_config_progress' in process.env); if (!progress) args.push('--no-progress'); if (verbose) args.push('--verbose'); args.push(...deps); if (verbose) console.log(`> npm ${args.join(' ')}`); if (list === undefined) list = process.env.npm_config_list !== ''; if (dryRun === undefined) dryRun = process.env.npm_config_dry_run; if (dryRun) { const duration = Math.trunc(performance.now() - start); const suffix = deps.length > 1 ? 's' : ''; console.log(`\nupdated ${deps.length} package${suffix} in ${duration}ms`); if (list) listDeps(deps); return } await spawnProcess('npm', args, { cwd }); deps = await resolveDeps(deps, root, true, verbose); if (list) listDeps(deps); if (save !== false && newDeps && newDeps.length) { await upgradeDeps(newDeps, config, root, lineBreak, verbose); } } async function downgradeDeps(deps, config, root, lineBreak, verbose) { let pkg; ({ config, pkg } = await resolveConfig('updates', config, root, undefined, undefined, verbose)); const { updateDependencies } = pkg; if (!updateDependencies || !updateDependencies.length) return pkg.updateDependencies = updateDependencies.filter(dep => !deps.includes(dep)); await writeJSON(config, pkg, lineBreak, verbose); } async function downgradeDependencies(depNames, { config, cwd, save, lineBreak, progress, list, verbose, dryRun } = {}) { const start = performance.now(); if (verbose === undefined) verbose = process.env.npm_config_loglevel === 'verbose'; if (!(depNames && depNames.length)) { return console.log('no dependencies to downgrade') } const root = await findRoot(cwd, verbose); const { packages } = await readJSON(path.join(root, 'package-lock.json'), verbose); const deps = depNames.reduce((result, pattern) => { if (isPattern(pattern)) { if (verbose) log(`looking for dependencies matching ${pattern}`); const match = picomatch(pattern); let updated; for (const key in packages) { if (key.startsWith('node_modules/')) { const name = key.substring(13); if (match(name)) { const pkg = packages[key]; const dep = `${name}@${pkg.version}`; if (verbose) log(`dependency ${dep} matched`); result.push(dep); updated = true; } } } if (!updated) throw new Error(`no dependency matched ${pattern}`) } else { const pkg = packages[`node_modules/${pattern}`]; if (!pkg) throw new Error(`unknown dependency: "${pattern}`) const dep = `${pattern}@${pkg.version}`; if (verbose) log(`dependency ${dep} found`); result.push(dep); } return result }, []); const args = ['i', '--no-save', '--no-audit', '--no-update-notifier']; if (progress === undefined) progress = !('npm_config_progress' in process.env); if (!progress) args.push('--no-progress'); if (verbose) args.push('--verbose'); args.push(...deps); if (verbose) console.log(`> npm ${args.join(' ')}`); if (list === undefined) list = process.env.npm_config_list !== ''; if (dryRun === undefined) dryRun = process.env.npm_config_dry_run; if (dryRun) { const duration = Math.trunc(performance.now() - start); const suffix = deps.length > 1 ? 's' : ''; console.log(`\ndowngraded ${deps.length} package${suffix} in ${duration}ms`); if (list) listDeps(deps); return } await spawnProcess('npm', args, { cwd }); if (list) listDeps(deps); if (save !== false) { await downgradeDeps(depNames, config, root, lineBreak, verbose); } } async function readPkg(verbose) { const __dirname = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))); return await readJSON(path.join(__dirname, '../package.json'), verbose) } async function installTool(name, root, list, progress, verbose, dryRun, start) { const args = ['i', '-D']; if (progress === false) args.push('--no-progress'); if (verbose) args.push('--verbose'); args.push(name); if (verbose) console.log(`> npm ${args.join(' ')}`); if (dryRun) { const duration = Math.trunc(performance.now() - start); console.log(`\nadded 1 package in ${duration}ms`); if (list !== false) { const { version } = await readPkg(verbose); listDeps({ [name]: version }); } } else { await spawnProcess('npm', args, { cwd }); if (list !== false) { const { [name]: version } = await resolveDeps([name], root, true, verbose); listDeps({ [name]: version }); } } } async function setupUpdateDependencies({ config, cwd, lineBreak, progress, list, verbose, dryRun } = {}) { const start = performance.now(); const root = await findRoot(cwd, verbose); const pkg = path.join(root, 'package.json'); if (config === true) config = path.join(root, 'package-updates.json'); else if (!config) config = pkg; else config = path.resolve(config); const same = config === pkg; if (verbose) log(`expecting configuration in "${shorten(config)}"`); const proj = await readJSON(pkg, verbose); let deps; if (same) { deps = proj; } else { try { deps = await readJSON(config, verbose); } catch (err) { if (err.code !== 'ENOENT') throw err if (verbose) log(`"${shorten(config)}" not found`); } } let saveDeps; if (!deps) { deps = { updateDependencies: [] }; saveDeps = true; } else if (!deps.updateDependencies) { deps.updateDependencies = []; saveDeps = true; } if (saveDeps && verbose) log(`creating empty configuration`); let saveProj; const { scripts, dependencies, devDependencies } = proj; if (!scripts) { proj.scripts = { prepare: 'dep-update up' }; saveProj = true; } else { const { prepare } = scripts; if (!prepare) { scripts.prepare = 'dep-update up'; saveProj = true; } else if (!/\bdep-update\b/.test(prepare)) { scripts.prepare = `dep-update up && ${prepare}`; saveProj = true; } } if (saveProj && verbose) log(`setting scripts.prepare to "${proj.scripts.prepare}"`); if (dryRun === undefined) dryRun = process.env.npm_config_dry_run; if (!dryRun) { if (same) { if (saveDeps || saveProj) await writeJSON(config, deps, lineBreak, verbose); } else { await Promise.all([ saveDeps && await writeJSON(config, deps, lineBreak, verbose), saveProj && await writeJSON(pkg, proj, lineBreak, verbose) ]); } } if (!(dependencies && dependencies['@pkgdep/update'] || devDependencies && devDependencies['@pkgdep/update'])) { await installTool('@pkgdep/update', root, list, progress, verbose, dryRun, start); } } exports.downgradeDependencies = downgradeDependencies; exports.setupUpdateDependencies = setupUpdateDependencies; exports.upgradeDependencies = upgradeDependencies; //# sourceMappingURL=index.cjs.map