File: main.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 (154 lines) | stat: -rw-r--r-- 5,795 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import {BaseContext, Builtins, Cli, Command, Option, UsageError} from 'clipanion';

import {version as corepackVersion}                              from '../package.json';

import {Engine}                                                  from './Engine';
import {DisableCommand}                                          from './commands/Disable';
import {EnableCommand}                                           from './commands/Enable';
import {InstallGlobalCommand}                                    from './commands/InstallGlobal';
import {InstallLocalCommand}                                     from './commands/InstallLocal';
import {PackCommand}                                             from './commands/Pack';
import {UpCommand}                                               from './commands/Up';
import {UseCommand}                                              from './commands/Use';
import {HydrateCommand}                                          from './commands/deprecated/Hydrate';
import {PrepareCommand}                                          from './commands/deprecated/Prepare';
import * as corepackUtils                                        from './corepackUtils';
import * as miscUtils                                            from './miscUtils';
import * as specUtils                                            from './specUtils';
import {Locator, SupportedPackageManagers, Descriptor}           from './types';

export type CustomContext = {cwd: string, engine: Engine};
export type Context = BaseContext & CustomContext;

type PackageManagerRequest = {
  packageManager: SupportedPackageManagers;
  binaryName: string;
  binaryVersion: string | null;
};

function getPackageManagerRequestFromCli(parameter: string | undefined, context: CustomContext & Partial<Context>): PackageManagerRequest | null {
  if (!parameter)
    return null;

  const match = parameter.match(/^([^@]*)(?:@(.*))?$/);
  if (!match)
    return null;

  const [, binaryName, binaryVersion] = match;
  const packageManager = context.engine.getPackageManagerFor(binaryName);
  if (!packageManager)
    return null;

  return {
    packageManager,
    binaryName,
    binaryVersion: binaryVersion || null,
  };
}

async function executePackageManagerRequest({packageManager, binaryName, binaryVersion}: PackageManagerRequest, args: Array<string>, context: Context) {
  const defaultVersion = await context.engine.getDefaultVersion(packageManager);
  const definition = context.engine.config.definitions[packageManager]!;

  // If all leading segments match one of the patterns defined in the `transparent`
  // key, we tolerate calling this binary even if the local project isn't explicitly
  // configured for it, and we use the special default version if requested.
  let isTransparentCommand = false;
  for (const transparentPath of definition.transparent.commands) {
    if (transparentPath[0] === binaryName && transparentPath.slice(1).every((segment, index) => segment === args[index])) {
      isTransparentCommand = true;
      break;
    }
  }

  const fallbackReference = isTransparentCommand
    ? definition.transparent.default ?? defaultVersion
    : defaultVersion;

  const fallbackLocator: Locator = {
    name: packageManager,
    reference: fallbackReference,
  };

  let descriptor: Descriptor;
  try {
    descriptor = await specUtils.findProjectSpec(context.cwd, fallbackLocator, {transparent: isTransparentCommand});
  } catch (err) {
    if (err instanceof miscUtils.Cancellation) {
      return 1;
    } else {
      throw err;
    }
  }

  if (binaryVersion)
    descriptor.range = binaryVersion;

  const resolved = await context.engine.resolveDescriptor(descriptor, {allowTags: true});
  if (resolved === null)
    throw new UsageError(`Failed to successfully resolve '${descriptor.range}' to a valid ${descriptor.name} release`);

  const installSpec = await context.engine.ensurePackageManager(resolved);

  return await corepackUtils.runVersion(resolved, installSpec, binaryName, args);
}

export async function runMain(argv: Array<string>) {
  // Because we load the binaries in the same process, we don't support custom contexts.
  const context = {
    ...Cli.defaultContext,
    cwd: process.cwd(),
    engine: new Engine(),
  };

  const [firstArg, ...restArgs] = argv;
  const request = getPackageManagerRequestFromCli(firstArg, context);

  let code: number;

  if (!request) {
    // If the first argument doesn't match any supported package manager, we fallback to the standard Corepack CLI
    const cli = new Cli({
      binaryLabel: `Corepack`,
      binaryName: `corepack`,
      binaryVersion: corepackVersion,
    });

    cli.register(Builtins.HelpCommand);
    cli.register(Builtins.VersionCommand);

    cli.register(DisableCommand);
    cli.register(EnableCommand);
    cli.register(InstallGlobalCommand);
    cli.register(InstallLocalCommand);
    cli.register(PackCommand);
    cli.register(UpCommand);
    cli.register(UseCommand);

    // Deprecated commands
    cli.register(HydrateCommand);
    cli.register(PrepareCommand);

    code = await cli.run(argv, context);
  } else {
    // Otherwise, we create a single-command CLI to run the specified package manager (we still use Clipanion in order to pretty-print usage errors).
    const cli = new Cli({
      binaryLabel: `'${request.binaryName}', via Corepack`,
      binaryName: request.binaryName,
      binaryVersion: `corepack/${corepackVersion}`,
    });

    cli.register(class BinaryCommand extends Command<Context> {
      proxy = Option.Proxy();
      async execute() {
        return executePackageManagerRequest(request, this.proxy, this.context);
      }
    });

    code = await cli.run(restArgs, context);
  }

  if (code !== 0) {
    process.exitCode ??= code;
  }
}