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