|
1 | 1 | import fs from 'fs-extra'; |
2 | | -import tar from 'tar'; |
3 | | - |
| 2 | +import chalk from 'chalk'; |
4 | 3 | // @ts-ignore |
5 | | -import RegClient from 'npm-registry-client'; |
6 | | - |
7 | | -// TODO: dependency inject this instance |
8 | | -const client = new RegClient(); |
9 | | -const npmUri = 'https://registry.npmjs.org/'; |
| 4 | +import spawn from 'spawndamnit'; |
10 | 5 |
|
11 | | -interface PublishPackageOptions { |
12 | | - metadata: Record<string, string>; |
13 | | - access: 'public' | 'restricted'; |
14 | | - token: string; |
15 | | - body: any; |
| 6 | +function jsonParse(input: string) { |
| 7 | + try { |
| 8 | + return JSON.parse(input); |
| 9 | + } catch (err) { |
| 10 | + if (err instanceof SyntaxError) { |
| 11 | + console.error('error parsing json:', input); |
| 12 | + } |
| 13 | + throw err; |
| 14 | + } |
16 | 15 | } |
17 | 16 |
|
18 | | -function publishPackage( |
19 | | - packageName: string, |
20 | | - { metadata, access, body, token }: PublishPackageOptions, |
| 17 | +async function publish( |
| 18 | + pkg: { dir: string; packageJson: Record<string, any> }, |
| 19 | + opts: { dry?: boolean }, |
| 20 | + authToken: string, |
21 | 21 | ) { |
22 | | - return new Promise<void>((resolve, reject) => |
23 | | - client.publish( |
24 | | - npmUri, |
25 | | - { metadata, access, body, auth: { token } }, |
26 | | - (error: any) => { |
27 | | - if (error) { |
28 | | - reject( |
29 | | - `Unexpected error when publishing ${packageName} to NPM: ${error}`, |
30 | | - ); |
31 | | - } |
32 | | - resolve(); |
33 | | - }, |
34 | | - ), |
| 22 | + const { name, version } = pkg.packageJson; |
| 23 | + |
| 24 | + console.log( |
| 25 | + `Publishing ${chalk.cyan(`"${name}"`)} at ${chalk.green(`"${version}"`)}`, |
35 | 26 | ); |
| 27 | + |
| 28 | + // Due to a super annoying issue in yarn, we have to manually override this env variable |
| 29 | + // See: https://github.com/yarnpkg/yarn/issues/2935#issuecomment-355292633 |
| 30 | + const envOverride = { |
| 31 | + // eslint-disable-next-line @typescript-eslint/camelcase |
| 32 | + npm_config_registry: `//registry.npmjs.org/:_authToken=${authToken}`, |
| 33 | + }; |
| 34 | + |
| 35 | + const publishOpts = ['--access', 'public']; |
| 36 | + |
| 37 | + if (opts?.dry) { |
| 38 | + publishOpts.push('--dry-run'); |
| 39 | + } |
| 40 | + |
| 41 | + const { stdout } = await spawn( |
| 42 | + 'npm', |
| 43 | + ['publish', pkg.dir, '--json', ...publishOpts], |
| 44 | + { |
| 45 | + env: Object.assign({}, process.env, envOverride), |
| 46 | + }, |
| 47 | + ); |
| 48 | + |
| 49 | + const json = jsonParse(stdout.toString().replace(/[^{]*/, '')); |
| 50 | + |
| 51 | + if (json.error) { |
| 52 | + throw new Error( |
| 53 | + `An error occurred while publishing ${name}: ${json.error.code} |
| 54 | +${json.error.summary} |
| 55 | +${json.error.detail ? '\n' + json.error.detail : ''} |
| 56 | + `, |
| 57 | + ); |
| 58 | + } |
36 | 59 | } |
37 | 60 |
|
38 | | -export default function publishPackages(path: string, authToken: string) { |
| 61 | +export default function publishPackages( |
| 62 | + path: string, |
| 63 | + opts: { dry?: boolean }, |
| 64 | + authToken: string, |
| 65 | +) { |
39 | 66 | return Promise.all( |
40 | 67 | fs.readdirSync(path).map(async dir => { |
41 | | - const packageName = `@codeshift/mod-${dir |
42 | | - .replace('@', '') |
43 | | - .replace('/', '__')}`; |
44 | 68 | const packagePath = `${path}/${dir}`; |
45 | | - const packageJson = await fs.readFile(`${packagePath}/package.json`); |
46 | | - const tarballPath = `${packagePath}/tarball.tgz`; |
| 69 | + const packageJson = await fs.readFile( |
| 70 | + `${packagePath}/package.json`, |
| 71 | + 'utf8', |
| 72 | + ); |
47 | 73 |
|
48 | | - await tar.create( |
| 74 | + await publish( |
49 | 75 | { |
50 | | - cwd: packagePath, |
51 | | - file: tarballPath, |
52 | | - gzip: true, |
| 76 | + dir: packagePath, |
| 77 | + packageJson: jsonParse(packageJson), |
53 | 78 | }, |
54 | | - ['.'], |
| 79 | + opts, |
| 80 | + authToken, |
55 | 81 | ); |
56 | | - |
57 | | - await publishPackage(packageName, { |
58 | | - // @ts-ignore |
59 | | - metadata: JSON.parse(packageJson), |
60 | | - access: 'public', |
61 | | - body: fs.createReadStream(tarballPath), |
62 | | - token: authToken, |
63 | | - }); |
64 | 82 | }), |
65 | | - ).catch(err => { |
66 | | - throw new Error(err); |
| 83 | + ).catch(error => { |
| 84 | + throw new Error(error); |
67 | 85 | }); |
68 | 86 | } |
0 commit comments