first commit

This commit is contained in:
2025-10-26 23:10:15 +08:00
commit 8f0345b7be
14961 changed files with 2356381 additions and 0 deletions

126
node_modules/release-it/lib/plugin/GitBase.js generated vendored Normal file
View File

@@ -0,0 +1,126 @@
import { EOL } from 'node:os';
import { format, parseGitUrl } from '../util.js';
import Plugin from './Plugin.js';
const options = { write: false };
const changelogFallback = 'git log --pretty=format:"* %s (%h)"';
class GitBase extends Plugin {
async init() {
const remoteUrl = await this.getRemoteUrl();
await this.fetch(remoteUrl);
const branchName = await this.getBranchName();
const repo = parseGitUrl(remoteUrl);
this.setContext({ remoteUrl, branchName, repo });
this.config.setContext({ remoteUrl, branchName, repo });
const latestTag = await this.getLatestTagName();
const secondLatestTag = !this.config.isIncrement ? await this.getSecondLatestTagName(latestTag) : null;
const tagTemplate = this.options.tagName || ((latestTag || '').match(/^v/) ? 'v${version}' : '${version}');
this.config.setContext({ latestTag, secondLatestTag, tagTemplate });
}
getName() {
const repo = this.getContext('repo');
return repo.project;
}
getLatestVersion() {
const { tagTemplate, latestTag } = this.config.getContext();
const prefix = format(tagTemplate.replace(/\$\{version\}/, ''), this.config.getContext());
return latestTag ? latestTag.replace(prefix, '').replace(/^v/, '') : null;
}
async getChangelog() {
const { snapshot } = this.config.getContext();
const { latestTag, secondLatestTag } = this.config.getContext();
const context = { latestTag, from: latestTag, to: 'HEAD' };
const { changelog } = this.options;
if (!changelog) return null;
if (latestTag && !this.config.isIncrement) {
context.from = secondLatestTag;
context.to = `${latestTag}^1`;
}
// For now, snapshots do not get a changelog, as it often goes haywire (easy to add to release manually)
if (snapshot) return '';
if (!context.from && changelog.includes('${from}')) {
return this.exec(changelogFallback);
}
return this.exec(changelog, { context, options });
}
bump(version) {
const { tagTemplate } = this.config.getContext();
const context = Object.assign(this.config.getContext(), { version });
const tagName = format(tagTemplate, context) || version;
this.setContext({ version });
this.config.setContext({ tagName });
}
isRemoteName(remoteUrlOrName) {
return remoteUrlOrName && !remoteUrlOrName.includes('/');
}
async getRemoteUrl() {
const remoteNameOrUrl = this.options.pushRepo || (await this.getRemote()) || 'origin';
return this.isRemoteName(remoteNameOrUrl)
? this.exec(`git remote get-url ${remoteNameOrUrl}`, { options }).catch(() =>
this.exec(`git config --get remote.${remoteNameOrUrl}.url`, { options }).catch(() => null)
)
: remoteNameOrUrl;
}
async getRemote() {
const branchName = await this.getBranchName();
return branchName ? await this.getRemoteForBranch(branchName) : null;
}
getBranchName() {
return this.exec('git rev-parse --abbrev-ref HEAD', { options }).catch(() => null);
}
getRemoteForBranch(branch) {
return this.exec(`git config --get branch.${branch}.remote`, { options }).catch(() => null);
}
fetch(remoteUrl) {
return this.exec('git fetch').catch(err => {
this.debug(err);
throw new Error(`Unable to fetch from ${remoteUrl}${EOL}${err.message}`);
});
}
getLatestTagName() {
const context = Object.assign({}, this.config.getContext(), { version: '*' });
const match = format(this.options.tagMatch || this.options.tagName || '${version}', context);
const exclude = this.options.tagExclude ? ` --exclude=${format(this.options.tagExclude, context)}` : '';
if (this.options.getLatestTagFromAllRefs) {
return this.exec(
`git -c "versionsort.suffix=-" for-each-ref --count=1 --sort=-v:refname --format="%(refname:short)" refs/tags/${match}`,
{ options }
).then(
stdout => stdout || null,
() => null
);
} else {
return this.exec(`git describe --tags --match=${match} --abbrev=0${exclude}`, { options }).then(
stdout => stdout || null,
() => null
);
}
}
async getSecondLatestTagName(latestTag) {
const sha = await this.exec(`git rev-list ${latestTag || '--skip=1'} --tags --max-count=1`, {
options
});
return this.exec(`git describe --tags --abbrev=0 "${sha}^"`, { options }).catch(() => null);
}
}
export default GitBase;

65
node_modules/release-it/lib/plugin/GitRelease.js generated vendored Normal file
View File

@@ -0,0 +1,65 @@
import { pick, readJSON } from '../util.js';
import GitBase from './GitBase.js';
const defaultConfig = readJSON(new URL('../../config/release-it.json', import.meta.url));
class GitRelease extends GitBase {
static isEnabled(options) {
return options.release;
}
getInitialOptions(options) {
const baseOptions = super.getInitialOptions(...arguments);
const git = options.git || defaultConfig.git;
const gitOptions = pick(git, [
'tagExclude',
'tagName',
'tagMatch',
'getLatestTagFromAllRefs',
'pushRepo',
'changelog',
'commit'
]);
return Object.assign({}, gitOptions, baseOptions);
}
get token() {
const { tokenRef } = this.options;
return process.env[tokenRef] || null;
}
async beforeRelease() {
const { releaseNotes: script } = this.options;
const { changelog } = this.config.getContext();
const releaseNotes =
typeof script === 'function' || typeof script === 'string' ? await this.processReleaseNotes(script) : changelog;
this.setContext({ releaseNotes });
if (releaseNotes !== changelog) {
this.log.preview({ title: 'release notes', text: releaseNotes });
}
}
async processReleaseNotes(script) {
if (typeof script === 'function') {
const ctx = Object.assign({}, this.config.getContext(), { [this.namespace]: this.getContext() });
return script(ctx);
}
if (typeof script === 'string') {
return this.exec(script);
}
}
afterRelease() {
const { isReleased, releaseUrl, discussionUrl } = this.getContext();
if (isReleased) {
this.log.log(`🔗 ${releaseUrl}`);
}
if (discussionUrl) {
this.log.log(`🔗 ${discussionUrl}`);
}
}
}
export default GitRelease;

75
node_modules/release-it/lib/plugin/Plugin.js generated vendored Normal file
View File

@@ -0,0 +1,75 @@
import { debug } from 'node:util';
import merge from 'lodash.merge';
import { get } from '../util.js';
class Plugin {
static isEnabled() {
return true;
}
static disablePlugin() {
return null;
}
constructor({ namespace, options = {}, container = {} } = {}) {
this.namespace = namespace;
this.options = Object.freeze(this.getInitialOptions(options, namespace));
this.context = {};
this.config = container.config;
this.log = container.log;
this.shell = container.shell;
this.spinner = container.spinner;
this.prompt = container.prompt;
this.debug = debug(`release-it:${namespace}`);
}
getInitialOptions(options, namespace) {
return options[namespace] || {};
}
init() {}
getName() {}
getLatestVersion() {}
getChangelog() {}
getIncrement() {}
getIncrementedVersionCI() {}
getIncrementedVersion() {}
beforeBump() {}
bump() {}
beforeRelease() {}
release() {}
afterRelease() {}
getContext(path) {
const context = merge({}, this.options, this.context);
return path ? get(context, path) : context;
}
setContext(context) {
merge(this.context, context);
}
exec(command, { options, context = {} } = {}) {
const ctx = Object.assign(context, this.config.getContext(), { [this.namespace]: this.getContext() });
return this.shell.exec(command, options, ctx);
}
registerPrompts(prompts) {
this.prompt.register(prompts, this.namespace);
}
async showPrompt(options) {
options.namespace = this.namespace;
return this.prompt.show(options);
}
step(options) {
const context = Object.assign({}, this.config.getContext(), { [this.namespace]: this.getContext() });
const opts = Object.assign({}, options, { context });
const isException = this.config.isPromptOnlyVersion && ['incrementList', 'publish', 'otp'].includes(opts.prompt);
return this.config.isCI && !isException ? this.spinner.show(opts) : this.showPrompt(opts);
}
}
export default Plugin;

89
node_modules/release-it/lib/plugin/factory.js generated vendored Normal file
View File

@@ -0,0 +1,89 @@
import url from 'node:url';
import path from 'node:path';
import util from 'node:util';
import { createRequire } from 'node:module';
import { castArray } from '../util.js';
import Version from './version/Version.js';
import Git from './git/Git.js';
import GitLab from './gitlab/GitLab.js';
import GitHub from './github/GitHub.js';
import npm from './npm/npm.js';
const debug = util.debug('release-it:plugins');
const pluginNames = ['npm', 'git', 'github', 'gitlab', 'version'];
const plugins = {
version: Version,
git: Git,
gitlab: GitLab,
github: GitHub,
npm: npm
};
const load = async pluginName => {
let plugin = null;
try {
const module = await import(pluginName);
plugin = module.default;
} catch (err) {
debug(err);
try {
const module = await import(path.join(process.cwd(), pluginName));
plugin = module.default;
} catch (err) {
debug(err);
// In some cases or tests we might need to support legacy `require.resolve`
const require = createRequire(process.cwd());
const module = await import(url.pathToFileURL(require.resolve(pluginName, { paths: [process.cwd()] })));
plugin = module.default;
}
}
return [getPluginName(pluginName), plugin];
};
/** @internal */
export const getPluginName = pluginName => {
if (pluginName.startsWith('.')) {
return path.parse(pluginName).name;
}
return pluginName;
};
export let getPlugins = async (config, container) => {
const context = config.getContext();
const disabledPlugins = [];
const enabledExternalPlugins = [];
if (context.plugins) {
for (const [pluginName, pluginConfig] of Object.entries(context.plugins)) {
const [name, Plugin] = await load(pluginName);
const [namespace, options] = pluginConfig.length === 2 ? pluginConfig : [name, pluginConfig];
config.setContext({ [namespace]: options });
if (await Plugin.isEnabled(options)) {
const instance = new Plugin({ namespace, options: config.getContext(), container });
debug({ namespace, options: instance.options });
enabledExternalPlugins.push(instance);
disabledPlugins.push(...pluginNames.filter(p => castArray(Plugin.disablePlugin(options)).includes(p)));
}
}
}
const enabledPlugins = await pluginNames.reduce(async (result, plugin) => {
if (plugin in plugins && !disabledPlugins.includes(plugin)) {
const Plugin = plugins[plugin];
const pluginOptions = context[plugin];
if (await Plugin.isEnabled(pluginOptions)) {
const instance = new Plugin({ namespace: plugin, options: context, container });
debug({ namespace: plugin, options: instance.options });
(await result).push(instance);
}
}
return result;
}, []);
return [enabledPlugins, enabledExternalPlugins];
};

242
node_modules/release-it/lib/plugin/git/Git.js generated vendored Normal file
View File

@@ -0,0 +1,242 @@
import { EOL } from 'node:os';
import { spawn } from 'node:child_process';
import matcher from 'wildcard-match';
import { format, e, fixArgs, once, castArray } from '../../util.js';
import GitBase from '../GitBase.js';
import prompts from './prompts.js';
const noop = Promise.resolve();
const invalidPushRepoRe = /^\S+@/;
const options = { write: false };
const docs = 'https://git.io/release-it-git';
async function isGitRepo() {
return await new Promise(resolve => {
const process = spawn('git', ['rev-parse', '--git-dir']);
process.on('close', code => resolve(code === 0));
process.on('error', () => resolve(false));
});
}
class Git extends GitBase {
constructor(...args) {
super(...args);
this.registerPrompts(prompts);
}
static async isEnabled(options) {
return options !== false && (await isGitRepo());
}
async init() {
if (this.options.requireBranch && !(await this.isRequiredBranch(this.options.requireBranch))) {
throw e(`Must be on branch ${this.options.requireBranch}`, docs);
}
if (this.options.requireCleanWorkingDir && !(await this.isWorkingDirClean())) {
throw e(`Working dir must be clean.${EOL}Please stage and commit your changes.`, docs);
}
await super.init();
const remoteUrl = this.getContext('remoteUrl');
if (this.options.push && !remoteUrl) {
throw e(`Could not get remote Git url.${EOL}Please add a remote repository.`, docs);
}
if (this.options.requireUpstream && !(await this.hasUpstreamBranch())) {
throw e(`No upstream configured for current branch.${EOL}Please set an upstream branch.`, docs);
}
if (this.options.requireCommits && (await this.getCommitsSinceLatestTag(this.options.commitsPath)) === 0) {
throw e(`There are no commits since the latest tag.`, docs, this.options.requireCommitsFail);
}
}
rollback() {
this.log.info('Rolling back changes...');
const { tagName } = this.config.getContext();
const { isCommitted, isTagged } = this.getContext();
if (isTagged) {
this.log.info(`Deleting local tag ${tagName}`);
this.exec(`git tag --delete ${tagName}`);
}
this.log.info(`Resetting local changes made`);
this.exec(`git reset --hard HEAD${isCommitted ? '~1' : ''}`);
}
enableRollback() {
this.rollbackOnce = once(this.rollback.bind(this));
process.on('SIGINT', this.rollbackOnce);
process.on('exit', this.rollbackOnce);
}
disableRollback() {
if (this.rollbackOnce) {
process.removeListener('SIGINT', this.rollbackOnce);
process.removeListener('exit', this.rollbackOnce);
}
}
async beforeRelease() {
if (this.options.commit) {
if (this.options.requireCleanWorkingDir) {
this.enableRollback();
}
const changeSet = await this.status();
this.log.preview({ title: 'changeset', text: changeSet });
await this.stageDir();
}
}
async release() {
const { commit, tag, push } = this.options;
await this.step({ enabled: commit, task: () => this.commit(), label: 'Git commit', prompt: 'commit' });
await this.step({ enabled: tag, task: () => this.tag(), label: 'Git tag', prompt: 'tag' });
return !!(await this.step({ enabled: push, task: () => this.push(), label: 'Git push', prompt: 'push' }));
}
async isRequiredBranch() {
const branch = await this.getBranchName();
const requiredBranches = castArray(this.options.requireBranch);
const [branches, negated] = requiredBranches.reduce(
([p, n], b) => (b.startsWith('!') ? [p, [...n, b.slice(1)]] : [[...p, b], n]),
[[], []]
);
return (
(branches.length > 0 ? matcher(branches)(branch) : true) &&
(negated.length > 0 ? !matcher(negated)(branch) : true)
);
}
async hasUpstreamBranch() {
const ref = await this.exec('git symbolic-ref HEAD', { options });
const branch = await this.exec(`git for-each-ref --format="%(upstream:short)" ${ref}`, { options }).catch(
() => null
);
return Boolean(branch);
}
tagExists(tag) {
return this.exec(`git show-ref --tags --quiet --verify -- refs/tags/${tag}`, { options }).then(
() => true,
() => false
);
}
isWorkingDirClean() {
return this.exec('git diff --quiet HEAD', { options }).then(
() => true,
() => false
);
}
async getCommitsSinceLatestTag(commitsPath = '') {
const latestTagName = await this.getLatestTagName();
const ref = latestTagName ? `${latestTagName}..HEAD` : 'HEAD';
return this.exec(`git rev-list ${ref} --count ${commitsPath ? `-- ${commitsPath}` : ''}`, { options }).then(Number);
}
async getUpstreamArgs(pushRepo) {
if (pushRepo && !this.isRemoteName(pushRepo)) {
// Use (only) `pushRepo` if it's configured and looks like a url
return [pushRepo];
} else if (!(await this.hasUpstreamBranch())) {
// Start tracking upstream branch (`pushRepo` is a name if set)
return ['--set-upstream', pushRepo || 'origin', await this.getBranchName()];
} else if (pushRepo && !invalidPushRepoRe.test(pushRepo)) {
return [pushRepo];
} else {
return [];
}
}
stage(file) {
if (!file || !file.length) return noop;
const files = castArray(file);
return this.exec(['git', 'add', ...files]).catch(err => {
this.log.warn(`Could not stage ${files}`);
this.debug(err);
});
}
stageDir({ baseDir = '.' } = {}) {
const { addUntrackedFiles } = this.options;
return this.exec(['git', 'add', baseDir, addUntrackedFiles ? '--all' : '--update']);
}
reset(file) {
const files = castArray(file);
return this.exec(['git', 'checkout', 'HEAD', '--', ...files]).catch(err => {
this.log.warn(`Could not reset ${files}`);
this.debug(err);
});
}
status() {
return this.exec('git status --short --untracked-files=no', { options }).catch(() => null);
}
commit({ message = this.options.commitMessage, args = this.options.commitArgs } = {}) {
const msg = format(message, this.config.getContext());
const commitMessageArgs = msg ? ['--message', msg] : [];
return this.exec(['git', 'commit', ...commitMessageArgs, ...fixArgs(args)]).then(
() => this.setContext({ isCommitted: true }),
err => {
this.debug(err);
if (/nothing (added )?to commit/.test(err) || /nichts zu committen/.test(err)) {
this.log.warn('No changes to commit. The latest commit will be tagged.');
} else {
throw new Error(err);
}
}
);
}
tag({ name, annotation = this.options.tagAnnotation, args = this.options.tagArgs } = {}) {
const message = format(annotation, this.config.getContext());
const tagName = name || this.config.getContext('tagName');
return this.exec(['git', 'tag', '--annotate', '--message', message, ...fixArgs(args), tagName])
.then(() => this.setContext({ isTagged: true }))
.catch(err => {
const { latestTag, tagName } = this.config.getContext();
if (/tag '.+' already exists/.test(err) && latestTag === tagName) {
this.log.warn(`Tag "${tagName}" already exists`);
} else {
throw err;
}
});
}
async push({ args = this.options.pushArgs } = {}) {
const { pushRepo } = this.options;
const upstreamArgs = await this.getUpstreamArgs(pushRepo);
try {
const push = await this.exec(['git', 'push', ...fixArgs(args), ...upstreamArgs]);
this.disableRollback();
return push;
} catch (error) {
try {
await this.rollbackTagPush();
} catch (tagError) {
this.log.warn(`An error was encountered when trying to rollback the tag on the remote: ${tagError.message}`);
}
throw error;
}
}
async rollbackTagPush() {
const { isTagged } = this.getContext();
if (isTagged) {
const { tagName } = this.config.getContext();
this.log.info(`Rolling back remote tag push ${tagName}`);
await this.exec(`git push origin --delete ${tagName}`);
}
}
afterRelease() {
this.disableRollback();
}
}
export default Git;

19
node_modules/release-it/lib/plugin/git/prompts.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
import { format, truncateLines } from '../../util.js';
export default {
commit: {
type: 'confirm',
message: context => `Commit (${truncateLines(format(context.git.commitMessage, context), 1, ' [...]')})?`,
default: true
},
tag: {
type: 'confirm',
message: context => `Tag (${format(context.tagName, context)})?`,
default: true
},
push: {
type: 'confirm',
message: () => 'Push?',
default: true
}
};

471
node_modules/release-it/lib/plugin/github/GitHub.js generated vendored Normal file
View File

@@ -0,0 +1,471 @@
import { createReadStream, statSync } from 'node:fs';
import path from 'node:path';
import open from 'open';
import { Octokit } from '@octokit/rest';
import { glob } from 'tinyglobby';
import mime from 'mime-types';
import retry from 'async-retry';
import newGithubReleaseUrl from 'new-github-release-url';
import { ProxyAgent } from 'proxy-agent';
import { format, parseVersion, readJSON, e, castArray } from '../../util.js';
import Release from '../GitRelease.js';
import prompts from './prompts.js';
import { getCommitsFromChangelog, getResolvedIssuesFromChangelog, searchQueries } from './util.js';
/**
* @typedef {import('@octokit/rest').RestEndpointMethodTypes['repos']['createRelease']['parameters']} CreateReleaseOptions
*/
const pkg = readJSON(new URL('../../../package.json', import.meta.url));
const docs = 'https://git.io/release-it-github';
const RETRY_CODES = [408, 413, 429, 500, 502, 503, 504, 521, 522, 524];
const DEFAULT_RETRY_MIN_TIMEOUT = 1000;
const parseErrormsg = err => {
let msg = err;
if (err instanceof Error) {
const { status, message } = err;
const headers = err.response ? err.response.headers : {};
msg = `${headers?.status || status} (${message})`;
}
return msg;
};
const truncateBody = body => {
// https://github.com/release-it/release-it/issues/965
if (body && body.length >= 124000) return body.substring(0, 124000) + '...';
return body;
};
class GitHub extends Release {
constructor(...args) {
super(...args);
this.registerPrompts(prompts);
}
async init() {
await super.init();
const { skipChecks, tokenRef, web, update, assets } = this.options;
if (!this.token || web) {
if (!web) {
this.log.warn(`Environment variable "${tokenRef}" is required for automated GitHub Releases.`);
this.log.warn('Falling back to web-based GitHub Release.');
}
this.setContext({ isWeb: true });
return;
}
if (web && assets) {
this.log.warn('Assets are not included in web-based releases.');
}
if (!skipChecks) {
// If we're running on GitHub Actions, we can skip the authentication and
// collaborator checks. Ref: https://bit.ly/2vsyRzu
if (process.env.GITHUB_ACTIONS) {
this.setContext({ username: process.env.GITHUB_ACTOR });
} else {
if (!(await this.isAuthenticated())) {
throw e(`Could not authenticate with GitHub using environment variable "${tokenRef}".`, docs);
}
if (!(await this.isCollaborator())) {
const { repository } = this.getContext('repo');
const { username } = this.getContext();
throw e(`User ${username} is not a collaborator for ${repository}.`, docs);
}
}
}
if (update) {
const { latestTag } = this.config.getContext();
try {
const { id, upload_url, tag_name } = await this.getLatestRelease();
if (latestTag === tag_name) {
this.setContext({ isUpdate: true, isReleased: true, releaseId: id, upload_url });
} else {
this.setContext({ isUpdate: false });
}
} catch (error) {
this.setContext({ isUpdate: false });
}
if (!this.getContext('isUpdate')) {
this.log.warn(`GitHub release for tag ${latestTag} was not found. Creating new release.`);
}
}
}
async isAuthenticated() {
if (this.config.isDryRun) return true;
try {
this.log.verbose('octokit users#getAuthenticated');
const { data } = await this.client.users.getAuthenticated();
this.setContext({ username: data.login });
return true;
} catch (err) {
this.debug(err);
return false;
}
}
async isCollaborator() {
if (this.config.isDryRun) return true;
const { owner, project: repo } = this.getContext('repo');
const { username } = this.getContext();
try {
const options = { owner, repo, username };
this.log.verbose(`octokit repos#checkCollaborator (${username})`);
await this.client.repos.checkCollaborator(options);
return true;
} catch (err) {
this.debug(err);
return false;
}
}
async release() {
const { assets } = this.options;
const { isWeb, isUpdate } = this.getContext();
const { isCI } = this.config;
const type = isUpdate ? 'update' : 'create';
const publishMethod = `${type}Release`;
if (isWeb) {
const task = () => this.createWebRelease();
return this.step({ task, label: 'Generating link to GitHub Release web interface', prompt: 'release' });
} else if (isCI) {
await this.step({ task: () => this[publishMethod](), label: `GitHub ${type} release` });
await this.step({ enabled: assets, task: () => this.uploadAssets(), label: 'GitHub upload assets' });
return this.step({
task: () => (isUpdate ? Promise.resolve() : this.commentOnResolvedItems()),
label: 'GitHub comment on resolved items'
});
} else {
const release = async () => {
await this[publishMethod]();
await this.uploadAssets();
return isUpdate ? Promise.resolve(true) : this.commentOnResolvedItems();
};
return this.step({ task: release, label: `GitHub ${type} release`, prompt: 'release' });
}
}
handleError(err, bail) {
const message = parseErrormsg(err);
const githubError = new Error(message);
this.log.verbose(err.errors);
this.debug(err);
if (!RETRY_CODES.includes(err.status)) {
return bail(githubError);
}
throw githubError;
}
get client() {
if (this._client) return this._client;
const { proxy, timeout } = this.options;
const repo = this.getContext('repo');
const host = this.options.host || repo.host;
const isGitHub = host === 'github.com';
const baseUrl = `https://${isGitHub ? 'api.github.com' : host}${isGitHub ? '' : '/api/v3'}`;
const options = {
baseUrl,
auth: `token ${this.token}`,
userAgent: `release-it/${pkg.version}`,
log: this.config.isDebug ? console : {},
request: {
timeout
}
};
if (proxy) {
options.request.agent = new ProxyAgent(proxy);
}
const client = new Octokit(options);
this._client = client;
return client;
}
async getLatestRelease() {
const { owner, project: repo } = this.getContext('repo');
try {
const options = { owner, repo };
this.debug(options);
const response = await this.client.repos.listReleases({ owner, repo, per_page: 1, page: 1 });
this.debug(response.data[0]);
return response.data[0];
} catch (err) {
return this.handleError(err, () => {});
}
}
async getOctokitReleaseOptions(options = {}) {
const { owner, project: repo } = this.getContext('repo');
const {
releaseName,
draft = false,
preRelease = false,
autoGenerate = false,
makeLatest = true,
discussionCategoryName = undefined
} = this.options;
const { tagName } = this.config.getContext();
const { version, releaseNotes, isUpdate } = this.getContext();
const { isPreRelease } = parseVersion(version);
const name = format(releaseName, this.config.getContext());
const releaseNotesObject = this.options.releaseNotes;
const body = autoGenerate
? isUpdate
? null
: ''
: truncateBody(releaseNotesObject?.commit ? await this.renderReleaseNotes(releaseNotesObject) : releaseNotes);
/**
* @type {CreateReleaseOptions}
*/
const contextOptions = {
owner,
make_latest: makeLatest.toString(),
repo,
tag_name: tagName,
name,
body,
draft,
prerelease: isPreRelease || preRelease,
generate_release_notes: autoGenerate,
discussion_category_name: discussionCategoryName
};
return Object.assign(options, contextOptions);
}
retry(fn) {
const { retryMinTimeout } = this.options;
return retry(fn, {
retries: 2,
minTimeout: typeof retryMinTimeout === 'number' ? retryMinTimeout : DEFAULT_RETRY_MIN_TIMEOUT
});
}
async createRelease() {
const options = await this.getOctokitReleaseOptions();
const { isDryRun } = this.config;
this.log.exec(`octokit repos.createRelease "${options.name}" (${options.tag_name})`, { isDryRun });
if (isDryRun) {
this.setContext({ isReleased: true, releaseUrl: this.getReleaseUrlFallback(options.tag_name) });
return true;
}
return this.retry(async bail => {
try {
this.debug(options);
const response = await this.client.repos.createRelease(options);
this.debug(response.data);
const { html_url, upload_url, id, discussion_url } = response.data;
this.setContext({
isReleased: true,
releaseId: id,
releaseUrl: html_url,
upload_url,
discussionUrl: discussion_url
});
this.config.setContext({
isReleased: true,
releaseId: id,
releaseUrl: html_url,
upload_url,
discussionUrl: discussion_url
});
this.log.verbose(`octokit repos.createRelease: done (${response.headers.location})`);
return response.data;
} catch (err) {
return this.handleError(err, bail);
}
});
}
uploadAsset(filePath) {
const url = this.getContext('upload_url');
const name = path.basename(filePath);
const contentType = mime.contentType(name) || 'application/octet-stream';
const contentLength = statSync(filePath).size;
return this.retry(async bail => {
try {
const options = {
url,
data: createReadStream(filePath),
name,
headers: {
'content-type': contentType,
'content-length': contentLength
}
};
this.debug(options);
const response = await this.client.repos.uploadReleaseAsset(options);
this.debug(response.data);
this.log.verbose(`octokit repos.uploadReleaseAsset: done (${response.data.browser_download_url})`);
return response.data;
} catch (err) {
return this.handleError(err, bail);
}
});
}
uploadAssets() {
const { assets } = this.options;
const { isReleased } = this.getContext();
const context = this.config.getContext();
const { isDryRun } = this.config;
const patterns = castArray(assets).map(pattern => format(pattern, context));
this.log.exec('octokit repos.uploadReleaseAssets', patterns, { isDryRun });
if (!assets || !isReleased) {
return true;
}
return glob(patterns).then(files => {
if (!files.length) {
this.log.warn(`octokit repos.uploadReleaseAssets: did not find "${assets}" relative to ${process.cwd()}`);
}
if (isDryRun) return Promise.resolve();
return Promise.all(files.map(filePath => this.uploadAsset(filePath)));
});
}
getReleaseUrlFallback(tagName) {
const { host, repository } = this.getContext('repo');
return `https://${host}/${repository}/releases/tag/${tagName}`;
}
async generateWebUrl() {
const repo = this.getContext('repo');
const host = this.options.host || repo.host;
const isGitHub = host === 'github.com';
const options = await this.getOctokitReleaseOptions();
const url = newGithubReleaseUrl({
user: options.owner,
repo: options.repo,
tag: options.tag_name,
isPrerelease: options.prerelease,
title: options.name,
body: options.body
});
return isGitHub ? url : url.replace('github.com', host);
}
async createWebRelease() {
const { isCI } = this.config;
const { tagName } = this.config.getContext();
const url = await this.generateWebUrl();
if (isCI) {
this.setContext({ isReleased: true, releaseUrl: url });
} else {
const isWindows = process.platform === 'win32';
if (isWindows) this.log.info(`Opening ${url}`);
await open(url, { wait: isWindows });
this.setContext({ isReleased: true, releaseUrl: this.getReleaseUrlFallback(tagName) });
}
}
async updateRelease() {
const { isDryRun } = this.config;
const release_id = this.getContext('releaseId');
const options = await this.getOctokitReleaseOptions({ release_id });
this.log.exec(`octokit repos.updateRelease (${options.tag_name})`, { isDryRun });
if (isDryRun) return true;
return this.retry(async bail => {
try {
this.debug(options);
const response = await this.client.repos.updateRelease(options);
this.setContext({ releaseUrl: response.data.html_url });
this.debug(response.data);
this.log.verbose(`octokit repos.updateRelease: done (${response.headers.location})`);
return true;
} catch (err) {
return this.handleError(err, bail);
}
});
}
async commentOnResolvedItems() {
const { isDryRun } = this.config;
const { host, owner, project: repo } = this.getContext('repo');
const changelog = await this.getChangelog();
const { comments } = this.options;
const { submit, issue, pr } = comments ?? {};
const context = this.getContext();
if (!submit || !changelog) return true;
const shas = getCommitsFromChangelog(changelog);
const searchResults = await Promise.all(searchQueries(this.client, owner, repo, shas));
const mergedPullRequests = searchResults.flatMap(items => items.map(item => ({ type: 'pr', number: item.number })));
const hostURL = 'https://' + (this.options.host || host);
const resolvedIssues = getResolvedIssuesFromChangelog(hostURL, owner, repo, changelog);
for (const item of [...resolvedIssues, ...mergedPullRequests]) {
const { type, number } = item;
const comment = format(format(type === 'pr' ? pr : issue, context), context);
const url = `${hostURL}/${owner}/${repo}/${type === 'pr' ? 'pull' : 'issues'}/${number}`;
if (isDryRun) {
this.log.exec(`octokit issues.createComment (${url})`, { isDryRun });
return Promise.resolve();
}
try {
await this.client.issues.createComment({ owner, repo, issue_number: number, body: comment });
this.log.log(`● Commented on ${url}`);
} catch (error) {
this.log.log(`✕ Failed to comment on ${url}`);
}
}
}
async getCommits() {
const { owner, project: repo } = this.getContext('repo');
const { latestTag } = this.config.getContext();
this.debug({ owner, repo, base: latestTag, head: 'HEAD' });
const { data } = await this.client.repos.compareCommits({ owner, repo, base: latestTag, head: 'HEAD' });
return data.commits;
}
async renderReleaseNotes(releaseNotes) {
const { commit: template, excludeMatches = [] } = releaseNotes;
const commits = await this.getCommits();
if (this.options.commit) commits.pop();
return commits
.map(commit => {
commit.commit.subject = commit.commit.message.split('\n')[0];
const partial = template.replace(/(?<!\$)\{((?:[^{}]|\${[^}]+})+)\}/g, (_, block) => {
const rendered = format(block, commit);
return excludeMatches.some(match => rendered.includes(match)) ? '' : rendered;
});
return format(partial, commit);
})
.join('\n');
}
}
export default GitHub;

16
node_modules/release-it/lib/plugin/github/prompts.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
import { format } from '../../util.js';
const message = context => {
const { isPreRelease, github } = context;
const { releaseName, update } = github;
const name = format(releaseName, context);
return `${update ? 'Update' : 'Create a'} ${isPreRelease ? 'pre-' : ''}release on GitHub (${name})?`;
};
export default {
release: {
type: 'confirm',
message,
default: true
}
};

42
node_modules/release-it/lib/plugin/github/util.js generated vendored Normal file
View File

@@ -0,0 +1,42 @@
// Totally much borrowed from https://github.com/semantic-release/github/blob/master/lib/success.js
import issueParser from 'issue-parser';
/** @internal */
export const getSearchQueries = (base, commits, separator = '+') => {
const encodedSeparator = encodeURIComponent(separator);
return commits.reduce((searches, commit) => {
const lastSearch = searches[searches.length - 1];
if (lastSearch && encodeURIComponent(lastSearch).length + commit.length <= 256 - encodedSeparator.length) {
searches[searches.length - 1] = `${lastSearch}${separator}${commit}`;
} else {
searches.push(`${base}${separator}${commit}`);
}
return searches;
}, []);
};
export const searchQueries = (client, owner, repo, shas) =>
getSearchQueries(`repo:${owner}/${repo}+type:pr+is:merged`, shas).map(
async q => (await client.search.issuesAndPullRequests({ q, advanced_search: true })).data.items
);
export const getCommitsFromChangelog = changelog => {
const regex = /\(([a-f0-9]{7,})\)/i;
return changelog.split('\n').flatMap(message => {
const match = message.match(regex);
if (match) return match[1];
return [];
});
};
export const getResolvedIssuesFromChangelog = (host, owner, repo, changelog) => {
const parser = issueParser('github', { hosts: [host] });
return changelog
.split('\n')
.map(parser)
.flatMap(parsed => parsed.actions.close)
.filter(action => !action.slug || action.slug === `${owner}/${repo}`)
.map(action => ({ type: 'issue', number: parseInt(action.issue, 10) }));
};

338
node_modules/release-it/lib/plugin/gitlab/GitLab.js generated vendored Normal file
View File

@@ -0,0 +1,338 @@
import path from 'node:path';
import fs from 'node:fs'; // import fs here so it can be stubbed in tests
import { readFile } from 'node:fs/promises';
import { glob } from 'tinyglobby';
import { Agent } from 'undici';
import Release from '../GitRelease.js';
import { format, e, castArray } from '../../util.js';
import prompts from './prompts.js';
const docs = 'https://git.io/release-it-gitlab';
const noop = Promise.resolve();
class GitLab extends Release {
constructor(...args) {
super(...args);
this.registerPrompts(prompts);
this.assets = [];
const { secure } = this.options;
const certificateAuthorityFileRef = this.options.certificateAuthorityFileRef || 'CI_SERVER_TLS_CA_FILE';
const certificateAuthorityFile =
this.options.certificateAuthorityFile || process.env[certificateAuthorityFileRef] || null;
this.certificateAuthorityOption = {};
const needsCustomAgent = Boolean(secure === false || certificateAuthorityFile);
if (needsCustomAgent) {
this.certificateAuthorityOption.dispatcher = new Agent({
connect: {
rejectUnauthorized: secure,
ca: certificateAuthorityFile ? fs.readFileSync(certificateAuthorityFile) : undefined
}
});
}
}
async init() {
await super.init();
const { skipChecks, tokenRef, tokenHeader } = this.options;
const { repo } = this.getContext();
const hasJobToken = (tokenHeader || '').toLowerCase() === 'job-token';
const origin = this.options.origin || `https://${repo.host}`;
this.setContext({
id: encodeURIComponent(repo.repository),
origin,
baseUrl: `${origin}/api/v4`
});
if (skipChecks) return;
if (!this.token) {
throw e(`Environment variable "${tokenRef}" is required for GitLab releases.`, docs);
}
if (!hasJobToken) {
if (!(await this.isAuthenticated())) {
throw e(`Could not authenticate with GitLab using environment variable "${tokenRef}".`, docs);
}
if (!(await this.isCollaborator())) {
const { user, repo } = this.getContext();
throw e(`User ${user.username} is not a collaborator for ${repo.repository}.`, docs);
}
}
}
async isAuthenticated() {
if (this.config.isDryRun) return true;
const endpoint = `user`;
try {
const { id, username } = await this.request(endpoint, { method: 'GET' });
this.setContext({ user: { id, username } });
return true;
} catch (err) {
this.debug(err);
return false;
}
}
async isCollaborator() {
if (this.config.isDryRun) return true;
const { id, user } = this.getContext();
const endpoint = `projects/${id}/members/all/${user.id}`;
try {
const { access_level } = await this.request(endpoint, { method: 'GET' });
return access_level && access_level >= 30;
} catch (err) {
this.debug(err);
return false;
}
}
async beforeRelease() {
await super.beforeRelease();
await this.checkReleaseMilestones();
}
async checkReleaseMilestones() {
if (this.options.skipChecks) return;
const releaseMilestones = this.getReleaseMilestones();
if (releaseMilestones.length < 1) {
return;
}
this.log.exec(`gitlab releases#checkReleaseMilestones`);
const { id } = this.getContext();
const endpoint = `projects/${id}/milestones`;
const requests = releaseMilestones.map(milestone => {
const options = {
method: 'GET',
searchParams: {
title: milestone,
include_parent_milestones: true
}
};
return this.request(endpoint, options).then(response => {
if (!Array.isArray(response)) {
const { baseUrl } = this.getContext();
throw new Error(
`Unexpected response from ${baseUrl}/${endpoint}. Expected an array but got: ${JSON.stringify(response)}`
);
}
if (response.length === 0) {
const error = new Error(`Milestone '${milestone}' does not exist.`);
this.log.warn(error.message);
throw error;
}
this.log.verbose(`gitlab releases#checkReleaseMilestones: milestone '${milestone}' exists`);
});
});
try {
await Promise.allSettled(requests).then(results => {
for (const result of results) {
if (result.status === 'rejected') {
throw e('Missing one or more milestones in GitLab. Creating a GitLab release will fail.', docs);
}
}
});
} catch (err) {
this.debug(err);
throw err;
}
this.log.verbose('gitlab releases#checkReleaseMilestones: done');
}
getReleaseMilestones() {
const { milestones } = this.options;
return (milestones || []).map(milestone => format(milestone, this.config.getContext()));
}
async release() {
const glRelease = () => this.createRelease();
const glUploadAssets = () => this.uploadAssets();
if (this.config.isCI) {
await this.step({ enabled: this.options.assets, task: glUploadAssets, label: 'GitLab upload assets' });
return await this.step({ task: glRelease, label: 'GitLab release' });
} else {
const release = () => glUploadAssets().then(() => glRelease());
return await this.step({ task: release, label: 'GitLab release', prompt: 'release' });
}
}
async request(endpoint, options) {
const { baseUrl } = this.getContext();
this.debug(Object.assign({ url: `${baseUrl}/${endpoint}` }, options));
const method = (options.method || 'POST').toLowerCase();
const { tokenHeader } = this.options;
const url = `${baseUrl}/${endpoint}${options.searchParams ? `?${new URLSearchParams(options.searchParams)}` : ''}`;
const headers = {
'user-agent': 'webpro/release-it',
[tokenHeader]: this.token
};
// When using fetch() with FormData bodies, we should not set the Content-Type header.
// See: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_FormData_Objects#sending_files_using_a_formdata_object
if (!(options.body instanceof FormData)) {
headers['Content-Type'] = typeof options.json !== 'undefined' ? 'application/json' : 'text/plain';
}
const requestOptions = {
method,
headers,
...this.certificateAuthorityOption
};
try {
const response = await fetch(
url,
options.json || options.body
? {
...requestOptions,
body: options.json ? JSON.stringify(options.json) : options.body
}
: requestOptions
);
if (!response.ok) {
const body = await response.json();
throw new Error(body.error);
}
const body = await response.json();
this.debug(body);
return body;
} catch (err) {
this.debug(err);
throw err;
}
}
async createRelease() {
const { releaseName } = this.options;
const { tagName, branchName, git: { tagAnnotation } = {} } = this.config.getContext();
const { id, releaseNotes, repo, origin } = this.getContext();
const { isDryRun } = this.config;
const name = format(releaseName, this.config.getContext());
const tagMessage = format(tagAnnotation, this.config.getContext());
const description = releaseNotes || '-';
const releaseUrl = `${origin}/${repo.repository}/-/releases/${tagName}`;
const releaseMilestones = this.getReleaseMilestones();
this.log.exec(`gitlab releases#createRelease "${name}" (${tagName})`, { isDryRun });
if (isDryRun) {
this.setContext({ isReleased: true, releaseUrl });
return true;
}
const endpoint = `projects/${id}/releases`;
const options = {
json: {
name,
ref: branchName,
tag_name: tagName,
tag_message: tagMessage,
description
}
};
if (this.assets.length) {
options.json.assets = {
links: this.assets
};
}
if (releaseMilestones.length) {
options.json.milestones = releaseMilestones;
}
try {
const body = await this.request(endpoint, options);
const releaseUrlSelf = body._links?.self ?? releaseUrl;
this.log.verbose('gitlab releases#createRelease: done');
this.setContext({ isReleased: true, releaseUrl: releaseUrlSelf });
this.config.setContext({ isReleased: true, releaseUrl: releaseUrlSelf });
return true;
} catch (err) {
this.debug(err);
throw err;
}
}
async uploadAsset(filePath) {
const name = path.basename(filePath);
const { useIdsForUrls, useGenericPackageRepositoryForAssets, genericPackageRepositoryName } = this.options;
const { id, origin, repo, version, baseUrl } = this.getContext();
const endpoint = useGenericPackageRepositoryForAssets
? `projects/${id}/packages/generic/${genericPackageRepositoryName}/${version}/${name}`
: `projects/${id}/uploads`;
if (useGenericPackageRepositoryForAssets) {
const options = {
method: 'PUT',
body: await readFile(filePath)
};
try {
const body = await this.request(endpoint, options);
if (!(body.message && body.message == '201 Created')) {
throw new Error(`GitLab asset upload failed`);
}
this.log.verbose(`gitlab releases#uploadAsset: done (${endpoint})`);
this.assets.push({
name,
url: `${baseUrl}/${endpoint}`
});
} catch (err) {
this.debug(err);
throw err;
}
} else {
const body = new FormData();
const rawData = await readFile(filePath);
body.set('file', new Blob([rawData]), name);
const options = { body };
try {
const body = await this.request(endpoint, options);
this.log.verbose(`gitlab releases#uploadAsset: done (${body.url})`);
this.assets.push({
name,
url: useIdsForUrls ? `${origin}${body.full_path}` : `${origin}/${repo.repository}${body.url}`
});
} catch (err) {
this.debug(err);
throw err;
}
}
}
uploadAssets() {
const { assets } = this.options;
const { isDryRun } = this.config;
const context = this.config.getContext();
const patterns = castArray(assets).map(pattern => format(pattern, context));
this.log.exec('gitlab releases#uploadAssets', patterns, { isDryRun });
if (!assets) {
return noop;
}
return glob(patterns).then(files => {
if (!files.length) {
this.log.warn(`gitlab releases#uploadAssets: could not find "${assets}" relative to ${process.cwd()}`);
}
if (isDryRun) return Promise.resolve();
return Promise.all(files.map(filePath => this.uploadAsset(filePath)));
});
}
}
export default GitLab;

9
node_modules/release-it/lib/plugin/gitlab/prompts.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
import { format } from '../../util.js';
export default {
release: {
type: 'confirm',
message: context => `Create a release on GitLab (${format(context.gitlab.releaseName, context)})?`,
default: true
}
};

284
node_modules/release-it/lib/plugin/npm/npm.js generated vendored Normal file
View File

@@ -0,0 +1,284 @@
import path from 'node:path';
import semver from 'semver';
import urlJoin from 'url-join';
import Plugin from '../Plugin.js';
import { hasAccess, rejectAfter, parseVersion, readJSON, e, fixArgs } from '../../util.js';
import prompts from './prompts.js';
const docs = 'https://git.io/release-it-npm';
const options = { write: false };
const MANIFEST_PATH = './package.json';
const DEFAULT_TAG = 'latest';
const DEFAULT_TAG_PRERELEASE = 'next';
const NPM_BASE_URL = 'https://www.npmjs.com';
const NPM_PUBLIC_PATH = '/package';
const DEFAULT_TIMEOUT = 10;
class npm extends Plugin {
static isEnabled(options) {
return hasAccess(MANIFEST_PATH) && options !== false;
}
constructor(...args) {
super(...args);
this.registerPrompts(prompts);
}
async init() {
const { name, version: latestVersion, private: isPrivate, publishConfig } = readJSON(path.resolve(MANIFEST_PATH));
this.setContext({ name, latestVersion, private: isPrivate, publishConfig });
this.config.setContext({ npm: { name } });
const { publish, skipChecks } = this.options;
const timeout = Number(this.options.timeout ?? DEFAULT_TIMEOUT) * 1000;
if (publish === false || isPrivate) return;
if (skipChecks) return;
const validations = Promise.all([this.isRegistryUp(), this.isAuthenticated(), this.getLatestRegistryVersion()]);
await Promise.race([validations, rejectAfter(timeout, e(`Timed out after ${timeout}ms.`, docs))]);
const [isRegistryUp, isAuthenticated, latestVersionInRegistry] = await validations;
if (!isRegistryUp) {
throw e(`Unable to reach npm registry (timed out after ${timeout}ms).`, docs);
}
if (!isAuthenticated) {
throw e('Not authenticated with npm. Please `npm login` and try again.', docs);
}
if (!(await this.isCollaborator())) {
const { username } = this.getContext();
throw e(`User ${username} is not a collaborator for ${name}.`, docs);
}
if (!latestVersionInRegistry) {
this.log.warn('No version found in npm registry. Assuming new package.');
} else {
if (!semver.eq(latestVersion, latestVersionInRegistry)) {
this.log.warn(
`Latest version in registry (${latestVersionInRegistry}) does not match package.json (${latestVersion}).`
);
}
}
}
getName() {
return this.getContext('name');
}
getLatestVersion() {
return this.options.ignoreVersion ? null : this.getContext('latestVersion');
}
async bump(version) {
const tag = this.options.tag || (await this.resolveTag(version));
this.setContext({ version, tag });
if (!this.config.isIncrement) return false;
const { versionArgs, allowSameVersion } = this.options;
const args = [version, '--no-git-tag-version', allowSameVersion && '--allow-same-version', ...fixArgs(versionArgs)];
const task = () => this.exec(`npm version ${args.filter(Boolean).join(' ')}`);
return this.spinner.show({ task, label: 'npm version' });
}
release() {
if (this.options.publish === false) return false;
if (this.getContext('private')) return false;
const publish = () => this.publish({ otpCallback });
const otpCallback =
this.config.isCI && !this.config.isPromptOnlyVersion ? null : task => this.step({ prompt: 'otp', task });
return this.step({ task: publish, label: 'npm publish', prompt: 'publish' });
}
isRegistryUp() {
const registry = this.getRegistry();
const registryArg = registry ? ` --registry ${registry}` : '';
return this.exec(`npm ping${registryArg}`, { options }).then(
() => true,
err => {
if (/code E40[04]|404.*(ping not found|No content for path)/.test(err)) {
this.log.warn('Ignoring response from unsupported `npm ping` command.');
return true;
}
return false;
}
);
}
isAuthenticated() {
const registry = this.getRegistry();
const registryArg = registry ? ` --registry ${registry}` : '';
return this.exec(`npm whoami${registryArg}`, { options }).then(
output => {
const username = output ? output.trim() : null;
this.setContext({ username });
return true;
},
err => {
this.debug(err);
if (/code E40[04]/.test(err)) {
this.log.warn('Ignoring response from unsupported `npm whoami` command.');
return true;
}
return false;
}
);
}
async isCollaborator() {
const registry = this.getRegistry();
const registryArg = registry ? ` --registry ${registry}` : '';
const name = this.getName();
const { username } = this.getContext();
if (username === undefined) return true;
if (username === null) return false;
try {
let npmVersion = await this.exec('npm --version', { options });
let accessCommand;
if (semver.gt(npmVersion, '9.0.0')) {
accessCommand = 'npm access list collaborators --json';
} else {
accessCommand = 'npm access ls-collaborators';
}
const output = await this.exec(`${accessCommand} ${name}${registryArg}`, { options });
try {
const collaborators = JSON.parse(output);
const permissions = collaborators[username];
return permissions && permissions.includes('write');
} catch (err) {
this.debug(err);
return false;
}
} catch (err) {
this.debug(err);
if (/code E400/.test(err)) {
this.log.warn('Ignoring response from unsupported `npm access` command.');
} else {
this.log.warn(`Unable to verify if user ${username} is a collaborator for ${name}.`);
}
return true;
}
}
async getLatestRegistryVersion() {
const registry = this.getRegistry();
const registryArg = registry ? ` --registry ${registry}` : '';
const name = this.getName();
const latestVersion = this.getLatestVersion();
const tag = await this.resolveTag(latestVersion);
return this.exec(`npm show ${name}@${tag} version${registryArg}`, { options }).catch(() => null);
}
getRegistryPreReleaseTags() {
return this.exec(`npm view ${this.getName()} dist-tags --json`, { options }).then(
output => {
try {
const tags = JSON.parse(output);
return Object.keys(tags).filter(tag => tag !== DEFAULT_TAG);
} catch (err) {
this.debug(err);
return [];
}
},
() => []
);
}
getPackageUrl() {
const baseUrl = this.getRegistry() || NPM_BASE_URL;
const publicPath = this.getPublicPath() || NPM_PUBLIC_PATH;
return urlJoin(baseUrl, publicPath, this.getName());
}
getRegistry() {
const { publishConfig } = this.getContext();
const registries = publishConfig
? publishConfig.registry
? [publishConfig.registry]
: Object.keys(publishConfig)
.filter(key => key.endsWith('registry'))
.map(key => publishConfig[key])
: [];
return registries[0];
}
getPublicPath() {
const { publishConfig } = this.getContext();
return (publishConfig && publishConfig.publicPath) ?? '';
}
async guessPreReleaseTag() {
const [tag] = await this.getRegistryPreReleaseTags();
if (tag) {
return tag;
} else {
this.log.warn(`Unable to get pre-release tag(s) from npm registry. Using "${DEFAULT_TAG_PRERELEASE}".`);
return DEFAULT_TAG_PRERELEASE;
}
}
async resolveTag(version) {
const { tag } = this.options;
const { isPreRelease, preReleaseId } = parseVersion(version);
if (!isPreRelease) {
return DEFAULT_TAG;
} else {
return tag || preReleaseId || (await this.guessPreReleaseTag());
}
}
async publish({ otp = this.options.otp, otpCallback } = {}) {
const { publishPath = '.', publishArgs } = this.options;
const { private: isPrivate, tag = DEFAULT_TAG } = this.getContext();
const otpArg = otp ? `--otp ${otp}` : '';
const dryRunArg = this.config.isDryRun ? '--dry-run' : '';
const registry = this.getRegistry();
const registryArg = registry ? `--registry ${registry}` : '';
if (isPrivate) {
this.log.warn('Skip publish: package is private.');
return false;
}
const args = [publishPath, `--tag ${tag}`, otpArg, dryRunArg, registryArg, ...fixArgs(publishArgs)].filter(Boolean);
return this.exec(`npm publish ${args.join(' ')}`, { options })
.then(() => {
this.setContext({ isReleased: true });
})
.catch(err => {
this.debug(err);
if (this.config.isDryRun && /publish over the previously published version/.test(err)) {
return Promise.resolve();
}
if (/one-time pass/.test(err)) {
if (otp != null) {
this.log.warn('The provided OTP is incorrect or has expired.');
}
if (otpCallback) {
return otpCallback(otp => this.publish({ otp, otpCallback }));
}
}
throw err;
});
}
afterRelease() {
const { isReleased } = this.getContext();
if (isReleased) {
this.log.log(`🔗 ${this.getPackageUrl()}`);
}
}
}
export default npm;

12
node_modules/release-it/lib/plugin/npm/prompts.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
export default {
publish: {
type: 'confirm',
message: context =>
`Publish ${context.npm.name}${context.npm.tag === 'latest' ? '' : `@${context.npm.tag}`} to npm?`,
default: true
},
otp: {
type: 'input',
message: () => `Please enter OTP for npm:`
}
};

131
node_modules/release-it/lib/plugin/version/Version.js generated vendored Normal file
View File

@@ -0,0 +1,131 @@
import { styleText } from 'node:util';
import semver from 'semver';
import Plugin from '../Plugin.js';
const RELEASE_TYPES = ['patch', 'minor', 'major'];
const PRERELEASE_TYPES = ['prepatch', 'preminor', 'premajor'];
const CONTINUATION_TYPES = ['prerelease', 'pre'];
const ALL_RELEASE_TYPES = [...RELEASE_TYPES, ...PRERELEASE_TYPES, ...CONTINUATION_TYPES];
const CHOICES = {
latestIsPreRelease: [CONTINUATION_TYPES[0], ...RELEASE_TYPES],
preRelease: PRERELEASE_TYPES,
default: [...RELEASE_TYPES, ...PRERELEASE_TYPES]
};
const getIncrementChoices = context => {
const { latestIsPreRelease, isPreRelease, preReleaseId, preReleaseBase } = context.version;
const types = latestIsPreRelease ? CHOICES.latestIsPreRelease : isPreRelease ? CHOICES.preRelease : CHOICES.default;
const choices = types.map(increment => ({
name: `${increment} (${semver.inc(context.latestVersion, increment, preReleaseId, preReleaseBase)})`,
value: increment
}));
const otherChoice = {
name: 'Other, please specify...',
value: null
};
return [...choices, otherChoice];
};
const versionTransformer = context => input =>
semver.valid(input)
? semver.gt(input, context.latestVersion)
? styleText('green', input)
: styleText('red', input)
: styleText(['red', 'bold'], input);
const prompts = {
incrementList: {
type: 'list',
message: () => 'Select increment (next version):',
choices: context => getIncrementChoices(context),
pageSize: 9
},
version: {
type: 'input',
message: () => `Please enter a valid version:`,
transformer: context => versionTransformer(context),
validate: input => !!semver.valid(input) || 'The version must follow the semver standard.'
}
};
class Version extends Plugin {
constructor(...args) {
super(...args);
this.registerPrompts(prompts);
}
getIncrement(options) {
return options.increment;
}
getIncrementedVersionCI(options) {
return this.incrementVersion(options);
}
async getIncrementedVersion(options) {
const { isCI } = this.config;
const version = this.incrementVersion(options);
return version || (isCI ? null : await this.promptIncrementVersion(options));
}
promptIncrementVersion(options) {
return new Promise(resolve => {
this.step({
prompt: 'incrementList',
task: increment =>
increment
? resolve(this.incrementVersion(Object.assign({}, options, { increment })))
: this.step({ prompt: 'version', task: resolve })
});
});
}
isPreRelease(version) {
return Boolean(semver.prerelease(version));
}
isValid(version) {
return Boolean(semver.valid(version));
}
incrementVersion({ latestVersion, increment, isPreRelease, preReleaseId, preReleaseBase }) {
if (increment === false) return latestVersion;
const latestIsPreRelease = this.isPreRelease(latestVersion);
const isValidVersion = this.isValid(increment);
if (latestVersion) {
this.setContext({ latestIsPreRelease });
}
if (isValidVersion && semver.gte(increment, latestVersion)) {
return increment;
}
if (isPreRelease && !increment && latestIsPreRelease) {
return semver.inc(latestVersion, 'prerelease', preReleaseId, preReleaseBase);
}
if (this.config.isCI && !increment) {
if (isPreRelease) {
return semver.inc(latestVersion, 'prepatch', preReleaseId, preReleaseBase);
} else {
return semver.inc(latestVersion, 'patch');
}
}
const normalizedType = RELEASE_TYPES.includes(increment) && isPreRelease ? `pre${increment}` : increment;
if (ALL_RELEASE_TYPES.includes(normalizedType)) {
return semver.inc(latestVersion, normalizedType, preReleaseId, preReleaseBase);
}
const coercedVersion = !isValidVersion && semver.coerce(increment);
if (coercedVersion) {
this.log.warn(`Coerced invalid semver version "${increment}" into "${coercedVersion}".`);
return coercedVersion.toString();
}
}
}
export default Version;