'use strict'; var path = require('path'); var promises = require('fs/promises'); var child_process = require('child_process'); var url = require('url'); 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$1(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 addDeps(deps, versions, config, root, lineBreak, verbose) { let pkg; ({ config, pkg } = await resolveConfig('extras', config, root, undefined, undefined, verbose)); let { extraDependencies } = pkg; if (!extraDependencies) extraDependencies = pkg.extraDependencies = {}; for (const dep of deps) { const version = dep.indexOf('@', 1); const name = version > 0 ? dep.slice(0, version) : dep; extraDependencies[name] = versions[name]; } await writeJSON(config, pkg, lineBreak, verbose); } function log(message) { console.log('dep-extra', message); } async function installExtraDependencies(newDeps, { config, cwd, 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-extras.json'); } else { config = path.resolve(config); } deps = await readJSON(config, verbose); } else { root = await findRoot(cwd, verbose); deps = await loadConfig('extras', root, verbose); } deps = deps.extraDependencies; } if (deps) { if (!Array.isArray(deps)) { deps = Object.keys(deps).map(name => `${name}@${deps[name]}`); } else if (verbose) { log(`requested to install: ${deps.join(', ')}`); } } if (!(deps && deps.length)) { return console.log('no extra dependencies to install') } 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'); if (!root) root = await findRoot(cwd, verbose); const detectedDeps = await resolveDeps$1(deps, root, false, verbose); const depsToInstall = deps.filter(dep => { const versionIndex = dep.indexOf('@', 1); let name, version; if (versionIndex > 0) { name = dep.slice(0, versionIndex); version = dep.slice(versionIndex + 1); } else { name = dep; } return !(version && version === detectedDeps[name]) }); if (depsToInstall.length) { log(`actually installing: ${depsToInstall.join(', ')}`); args.push(...depsToInstall); 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 = depsToInstall.length > 1 ? 's' : ''; console.log(`\nadded ${depsToInstall.length} package${suffix} in ${duration}ms`); if (list) listDeps(depsToInstall); return } await spawnProcess('npm', args, { cwd }); deps = await resolveDeps$1(depsToInstall, root, true, verbose); if (list) listDeps(deps); if (save !== false && newDeps && newDeps.length) { await addDeps(newDeps, deps, config, root, lineBreak, verbose); } } else { log('nothing to actually install'); } } async function removeDeps(deps, config, root, lineBreak, verbose) { let pkg; ({ config, pkg } = await resolveConfig('extras', config, root, undefined, undefined, verbose)); const { extraDependencies } = pkg; if (!extraDependencies) return for (const name of deps) delete extraDependencies[name]; await writeJSON(config, pkg, lineBreak, verbose); } async function uninstallExtraDependencies(deps, { config, cwd, save, lineBreak, progress, list, verbose, dryRun } = {}) { const start = performance.now(); if (verbose === undefined) verbose = process.env.npm_config_loglevel === 'verbose'; if (!(deps && deps.length)) { return console.log('no extra dependencies to uninstall') } if (verbose) log(`requested to uninstall: ${deps.join(', ')}`); const args = ['r', '--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'); const root = await findRoot(cwd, verbose); const detectedDeps = await resolveDeps(deps, root, false, verbose); const depsToRemove = deps.filter(dep => { const version = dep.indexOf('@', 1); const name = version > 0 ? dep.slice(0, version) : dep; return detectedDeps[name]; }); if (depsToRemove.length) { log(`actually uninstalling: ${depsToRemove.join(', ')}`); args.push(...depsToRemove); 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 = depsToRemove.length > 1 ? 's' : ''; console.log(`\nremoved ${depsToRemove.length} package${suffix} in ${duration}ms`); if (list) listDeps(depsToRemove); return } await spawnProcess('npm', args, { cwd }); if (list) listDeps(depsToRemove); if (save !== false) { await removeDeps(depsToRemove, config, root, lineBreak, verbose); } } else { log('nothing to actually uninstall'); } } async function readPkg(verbose) { const __dirname = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.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$1([name], root, true, verbose); listDeps({ [name]: version }); } } } async function setupExtraDependencies({ 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-extras.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 = { extraDependencies: {} }; saveDeps = true; } else if (!deps.extraDependencies) { deps.extraDependencies = {}; saveDeps = true; } if (saveDeps && verbose) log(`creating empty configuration`); let saveProj; const { scripts, dependencies, devDependencies } = proj; if (!scripts) { proj.scripts = { prepare: 'dep-extra i' }; saveProj = true; } else { const { prepare } = scripts; if (!prepare) { scripts.prepare = 'dep-extra i'; saveProj = true; } else if (!/\bdep-extra\b/.test(prepare)) { scripts.prepare = `dep-extra i && ${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/extra'] || devDependencies && devDependencies['@pkgdep/extra'])) { await installTool('@pkgdep/extra', root, list, progress, verbose, dryRun, start); } } exports.installExtraDependencies = installExtraDependencies; exports.setupExtraDependencies = setupExtraDependencies; exports.uninstallExtraDependencies = uninstallExtraDependencies; //# sourceMappingURL=index.cjs.map