File: InstallGlobal.ts

package info (click to toggle)
node-corepack 0.24.0-5
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 262,916 kB
  • sloc: javascript: 94; makefile: 18; sh: 12
file content (132 lines) | stat: -rw-r--r-- 4,660 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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});
        }
      }
    }
  }
}