1import { findYarnOrNpmWorkspaceRoot, YARN_LOCK_FILE } from '../utils/nodeWorkspaces';
2import { createPendingSpawnAsync } from '../utils/spawn';
3import { isYarnOfflineAsync } from '../utils/yarn';
4import { BasePackageManager } from './BasePackageManager';
5
6export class YarnPackageManager extends BasePackageManager {
7  readonly name = 'yarn';
8  readonly bin = 'yarnpkg';
9  readonly lockFile = YARN_LOCK_FILE;
10
11  /** Check if Yarn is running in offline mode, and add the `--offline` flag */
12  private async withOfflineFlagAsync(namesOrFlags: string[]): Promise<string[]> {
13    return (await isYarnOfflineAsync()) ? [...namesOrFlags, '--offline'] : namesOrFlags;
14  }
15
16  workspaceRoot() {
17    const root = findYarnOrNpmWorkspaceRoot(this.ensureCwdDefined('workspaceRoot'));
18    if (root) {
19      return new YarnPackageManager({
20        ...this.options,
21        silent: this.silent,
22        log: this.log,
23        cwd: root,
24      });
25    }
26
27    return null;
28  }
29
30  installAsync(flags: string[] = []) {
31    return createPendingSpawnAsync(
32      () => this.withOfflineFlagAsync(['install']),
33      (args) => this.runAsync([...args, ...flags])
34    );
35  }
36
37  addAsync(namesOrFlags: string[] = []) {
38    if (!namesOrFlags.length) {
39      return this.installAsync();
40    }
41
42    return createPendingSpawnAsync(
43      () => this.withOfflineFlagAsync(['add', ...namesOrFlags]),
44      (args) => this.runAsync(args)
45    );
46  }
47
48  addDevAsync(namesOrFlags: string[] = []) {
49    if (!namesOrFlags.length) {
50      return this.installAsync();
51    }
52
53    return createPendingSpawnAsync(
54      () => this.withOfflineFlagAsync(['add', '--dev', ...namesOrFlags]),
55      (args) => this.runAsync(args)
56    );
57  }
58
59  addGlobalAsync(namesOrFlags: string[] = []) {
60    if (!namesOrFlags.length) {
61      return this.installAsync();
62    }
63
64    return createPendingSpawnAsync(
65      () => this.withOfflineFlagAsync(['global', 'add', ...namesOrFlags]),
66      (args) => this.runAsync(args)
67    );
68  }
69
70  removeAsync(namesOrFlags: string[]) {
71    return this.runAsync(['remove', ...namesOrFlags]);
72  }
73
74  removeDevAsync(namesOrFlags: string[]) {
75    return this.runAsync(['remove', ...namesOrFlags]);
76  }
77
78  removeGlobalAsync(namesOrFlags: string[]) {
79    return this.runAsync(['global', 'remove', ...namesOrFlags]);
80  }
81}
82