xref: /expo/tools/src/Npm.ts (revision 95665447)
1import fs from 'fs-extra';
2import glob from 'glob-promise';
3
4import { spawnAsync, spawnJSONCommandAsync, SpawnOptions } from './Utils';
5
6export const EXPO_DEVELOPERS_TEAM_NAME = 'expo:developers';
7
8export type PackageViewType = null | {
9  name: string;
10  version: string;
11  'dist-tags': {
12    latest: string;
13    [tag: string]: string;
14  };
15  versions: string[];
16  time: {
17    created: string;
18    modified: string;
19    [time: string]: string;
20  };
21  maintainers: string[];
22  description: string;
23  author: string;
24  gitHead: string;
25  dist: {
26    tarball: string;
27  };
28  // and more but these are the basic ones, we shouldn't need more.
29  [key: string]: unknown;
30};
31
32export type ProfileType = null | {
33  name: string;
34  email: string;
35  tfa: {
36    pending: boolean;
37    mode: string;
38  };
39  [key: string]: unknown;
40};
41
42/**
43 * Runs `npm view` for package with given name. Returns null if package is not published yet.
44 */
45export async function getPackageViewAsync(
46  packageName: string,
47  version?: string
48): Promise<PackageViewType> {
49  try {
50    return await spawnJSONCommandAsync('npm', [
51      'view',
52      version ? `${packageName}@${version}` : packageName,
53      '--json',
54    ]);
55  } catch {
56    return null;
57  }
58}
59
60/**
61 * Runs `npm profile get`. Returns null if user is not authenticated.
62 */
63export async function getProfileAsync(): Promise<ProfileType> {
64  try {
65    return await spawnJSONCommandAsync('npm', ['profile', 'get', '--json']);
66  } catch {
67    return null;
68  }
69}
70
71/**
72 * Download npm tarball
73 */
74export async function downloadPackageTarballAsync(
75  targetDir: string,
76  packageName: string,
77  version?: string
78): Promise<string> {
79  await fs.ensureDir(targetDir);
80  await spawnAsync('npm', ['pack', version ? `${packageName}@${version}` : packageName], {
81    cwd: targetDir,
82    stdio: 'ignore',
83  });
84  const result = await glob('*.tgz', { cwd: targetDir });
85  if (result.length === 0) {
86    throw new Error('Download tarball not found');
87  }
88  return result[0];
89}
90
91/**
92 * Publishes a package at given directory to the global npm registry.
93 */
94export async function publishPackageAsync(
95  packageDir: string,
96  tagName: string = 'latest',
97  dryRun: boolean = false,
98  spawnOptions: SpawnOptions = {}
99): Promise<void> {
100  const args = ['publish', '--tag', tagName, '--access', 'public'];
101
102  if (dryRun) {
103    args.push('--dry-run');
104  }
105  await spawnAsync('npm', args, {
106    cwd: packageDir,
107    ...spawnOptions,
108  });
109}
110
111/**
112 * Adds dist-tag to a specific version of the package.
113 */
114export async function addTagAsync(
115  packageName: string,
116  version: string,
117  tagName: string
118): Promise<void> {
119  await spawnAsync('npm', ['dist-tag', 'add', `${packageName}@${version}`, tagName]);
120}
121
122/**
123 * Removes package's tag with given name.
124 */
125export async function removeTagAsync(packageName: string, tagName: string): Promise<void> {
126  await spawnAsync('npm', ['dist-tag', 'rm', packageName, tagName]);
127}
128
129/**
130 * Gets a list of user names in the team with given team name.
131 */
132export async function getTeamMembersAsync(teamName: string): Promise<string[]> {
133  return await spawnJSONCommandAsync('npm', ['team', 'ls', teamName, '--json']);
134}
135
136/**
137 * Adds a package to organization team granting access to everyone in the team.
138 */
139export async function grantReadWriteAccessAsync(
140  packageName: string,
141  teamName: string
142): Promise<void> {
143  await spawnAsync('npm', ['access', 'grant', 'read-write', teamName, packageName]);
144}
145
146/**
147 * Returns a name of the currently logged in user or `null` if logged out.
148 */
149export async function whoamiAsync(): Promise<string | null> {
150  try {
151    const { stdout } = await spawnAsync('npm', ['whoami']);
152    return stdout.trim();
153  } catch {
154    return null;
155  }
156}
157