function install(deps, options = {}, exec) {
	const dev = options.dev !== false;
	const run = getRunFunction(options);

	// options.versions is a min versions mapping,
	// the list of packages to install will be taken from deps
	let versions = options.versions || {};

	/** @type string[] */
	let dependencies = [];

	if (typeof deps === 'string') {
		dependencies = [deps];
	} else if (Array.isArray(deps)) {
		dependencies = deps;
	} else if (typeof deps === 'object' && deps !== null) {
		// deps is an object with required versions
		// prettier-ignore
		versions = deps;
		dependencies = Object.keys(deps);
	}

	const newDeps = getUnsatisfiedDeps(dependencies, versions, { dev });
	if (newDeps.length === 0) {
		return;
	}

	log.info(`Installing ${listify(newDeps)}...`);
	const versionedDeps = newDeps.map(dep => getVersionedDep(dep, versions));

	// eslint-disable-next-line consistent-return
	return run(versionedDeps, { dev }, exec);
}
Install or update given npm packages if needed
@param {Record<string, string> | string[] | string} deps
@param {Options} [options]
@param {Function} exec