src/npm-utils.js
import execa from 'execa';
import normalizeUrl from 'normalize-url';
import path from 'path';
import rc from 'rc';
import getAuthToken from 'registry-auth-token';
import getRegistryUrl from 'registry-auth-token/registry-url';
import { outputFile, readFile } from 'fs-extra';
import nerfDart from 'nerf-dart';
import { getError } from './error';
const NPMX_DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org/';
// Set the environment variable `LEGACY_TOKEN` when user use the legacy auth, so it can be resolved by npm CLI
export function getLegacyToken({ NPM_EMAIL, NPM_PASSWORD, NPM_USERNAME } = {}) {
if (NPM_USERNAME && NPM_PASSWORD && NPM_EMAIL) {
return {
LEGACY_TOKEN: Buffer.from(`${NPM_USERNAME}:${NPM_PASSWORD}`, 'utf8').toString('base64'),
};
}
return {};
}
export async function setAuth(
npmrc,
registry,
{ cwd = '.', env: { NPM_TOKEN, NPM_EMAIL, NPM_CONFIG_USERCONFIG, LEGACY_TOKEN } = {}, logger },
) {
logger.log('Verify authentication for registry %s', registry);
const { configs, ...rcConfig } = rc(
'npm',
{ registry: NPMX_DEFAULT_NPM_REGISTRY },
{ config: NPM_CONFIG_USERCONFIG || path.resolve(cwd, '.npmrc') },
);
let currentConfig = '';
if (configs.length > 0) {
logger.log('Reading npm config from %s', configs.join(', '));
currentConfig = await Promise.all(configs.map(c => readFile(c)));
currentConfig = `${currentConfig.join('\n')}\n`;
}
if (getAuthToken(registry, { npmrc: rcConfig, recursive: true })) {
await outputFile(npmrc, currentConfig.trim());
} else if (LEGACY_TOKEN && NPM_EMAIL) {
await outputFile(npmrc, `${currentConfig}_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`);
logger.log(`Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to ${npmrc}`);
} else if (NPM_TOKEN) {
await outputFile(npmrc, `${currentConfig}${nerfDart(registry)}:_authToken = \${NPM_TOKEN}`);
logger.log(`Wrote NPM_TOKEN to ${npmrc}`);
} else {
throw getError('ENONPMTOKEN', { registry });
}
}
export function getReleasesInfo(
{ name },
{ env: { DEFAULT_NPM_REGISTRY = NPMX_DEFAULT_NPM_REGISTRY } = {}, nextRelease: { version } },
distTag,
registry,
) {
return {
name: `npm package (@${distTag} dist-tag)`,
url:
normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)
? `https://www.npmjs.com/package/${name}/v/${version}`
: undefined,
channel: distTag,
};
}
export function getRegistry(
{ publishConfig: { registry } = {}, name },
{ cwd = '.', env = {} } = {},
) {
return (
registry ||
env.NPM_CONFIG_REGISTRY ||
getRegistryUrl(
name.split('/')[0],
rc('npm', { registry: NPMX_DEFAULT_NPM_REGISTRY }, { config: path.resolve(cwd, '.npmrc') }),
)
);
}
export async function verifyNpmAuth(npmrc, context, pkg) {
const {
cwd,
stdout,
stderr,
env = { DEFAULT_NPM_REGISTRY: NPMX_DEFAULT_NPM_REGISTRY, ...context.env },
} = context;
const registry = getRegistry(pkg, context);
await setAuth(npmrc, registry, context);
if (
env.DEFAULT_NPM_REGISTRY &&
normalizeUrl(registry) === normalizeUrl(env.DEFAULT_NPM_REGISTRY)
) {
try {
const whoamiResult = execa('npm', ['whoami', '--userconfig', npmrc, '--registry', registry], {
cwd,
env,
});
whoamiResult.stdout.pipe(stdout, { end: false });
whoamiResult.stderr.pipe(stderr, { end: false });
await whoamiResult;
} catch (e) {
throw getError('EINVALIDNPMTOKEN', { registry });
}
}
}
export function summarizeReleasesInfo(releasesInfo) {
const validReleases = releasesInfo.filter(i => i !== false);
const urls = validReleases.filter(i => i.url !== undefined).map(i => i.url);
if (validReleases.length === 0) return false;
return {
name: validReleases[0].name,
urls,
channel: validReleases[0].channel,
};
}