mirror of https://github.com/nodejs/corepack.git
133 lines
4.6 KiB
TypeScript
133 lines
4.6 KiB
TypeScript
import {Command, Option, UsageError} from 'clipanion';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
import * as folderUtils from '../folderUtils';
|
|
import * as specUtils from '../specUtils';
|
|
import {Descriptor, Locator, isSupportedPackageManager} from '../types';
|
|
|
|
import {BaseCommand} from './Base';
|
|
|
|
export class InstallGlobalCommand extends BaseCommand {
|
|
static paths = [
|
|
[`install`],
|
|
];
|
|
|
|
static usage = Command.Usage({
|
|
description: `Install package managers on the system`,
|
|
details: `
|
|
Download the selected package managers and install them on the system.
|
|
|
|
Package managers thus installed will be configured as the new default when calling their respective binaries outside of projects defining the 'packageManager' field.
|
|
`,
|
|
examples: [[
|
|
`Install the latest version of Yarn 1.x and make it globally available`,
|
|
`corepack install -g yarn@^1`,
|
|
], [
|
|
`Install the latest version of all available package managers, and make them globally available`,
|
|
`corepack install -g --all`,
|
|
]],
|
|
});
|
|
|
|
global = Option.Boolean(`-g,--global`, {
|
|
required: true,
|
|
});
|
|
|
|
all = Option.Boolean(`--all`, false, {
|
|
description: `If true, all available default package managers will be installed`,
|
|
});
|
|
|
|
cacheOnly = Option.Boolean(`--cache-only`, false, {
|
|
description: `If true, the package managers will only be cached, not set as new defaults`,
|
|
});
|
|
|
|
args = Option.Rest();
|
|
|
|
async execute() {
|
|
if (this.args.length === 0 && !this.all)
|
|
throw new UsageError(`No package managers specified; use --all to install all available package managers, or specify one or more package managers to proceed`);
|
|
|
|
if (!this.all) {
|
|
for (const arg of this.args) {
|
|
if (arg.endsWith(`.tgz`)) {
|
|
await this.installFromTarball(path.resolve(this.context.cwd, arg));
|
|
} else {
|
|
await this.installFromDescriptor(specUtils.parseSpec(arg, `CLI arguments`, {enforceExactVersion: false}));
|
|
}
|
|
}
|
|
} else {
|
|
for (const descriptor of await this.context.engine.getDefaultDescriptors()) {
|
|
await this.installFromDescriptor(descriptor);
|
|
}
|
|
}
|
|
}
|
|
|
|
log(locator: Locator) {
|
|
if (this.cacheOnly) {
|
|
this.context.stdout.write(`Adding ${locator.name}@${locator.reference} to the cache...\n`);
|
|
} else {
|
|
this.context.stdout.write(`Installing ${locator.name}@${locator.reference}...\n`);
|
|
}
|
|
}
|
|
|
|
async installFromDescriptor(descriptor: Descriptor) {
|
|
const resolved = await this.context.engine.resolveDescriptor(descriptor, {allowTags: true, useCache: false});
|
|
if (resolved === null)
|
|
throw new UsageError(`Failed to successfully resolve '${descriptor.range}' to a valid ${descriptor.name} release`);
|
|
|
|
this.log(resolved);
|
|
await this.context.engine.ensurePackageManager(resolved);
|
|
|
|
if (!this.cacheOnly) {
|
|
await this.context.engine.activatePackageManager(resolved);
|
|
}
|
|
}
|
|
|
|
async installFromTarball(p: string) {
|
|
const installFolder = folderUtils.getInstallFolder();
|
|
|
|
const archiveEntries = new Map<string, Set<string>>();
|
|
const {default: tar} = await import(`tar`);
|
|
|
|
let hasShortEntries = false;
|
|
|
|
await tar.t({file: p, onentry: entry => {
|
|
const segments = entry.path.split(/\//g);
|
|
if (segments.length > 0 && segments[segments.length - 1] !== `.corepack`)
|
|
return;
|
|
|
|
|
|
if (segments.length < 3) {
|
|
hasShortEntries = true;
|
|
} else {
|
|
let references = archiveEntries.get(segments[0]);
|
|
if (typeof references === `undefined`)
|
|
archiveEntries.set(segments[0], references = new Set());
|
|
|
|
references.add(segments[1]);
|
|
}
|
|
}});
|
|
|
|
if (hasShortEntries || archiveEntries.size < 1)
|
|
throw new UsageError(`Invalid archive format; did it get generated by 'corepack pack'?`);
|
|
|
|
for (const [name, references] of archiveEntries) {
|
|
for (const reference of references) {
|
|
if (!isSupportedPackageManager(name))
|
|
throw new UsageError(`Unsupported package manager '${name}'`);
|
|
|
|
this.log({name, reference});
|
|
|
|
// Recreate the folder in case it was deleted somewhere else:
|
|
await fs.promises.mkdir(installFolder, {recursive: true});
|
|
|
|
await tar.x({file: p, cwd: installFolder}, [`${name}/${reference}`]);
|
|
|
|
if (!this.cacheOnly) {
|
|
await this.context.engine.activatePackageManager({name, reference});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|