release-it 18.1.2 → 19.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/config.js CHANGED
@@ -1,8 +1,11 @@
1
1
  import util from 'node:util';
2
2
  import { cosmiconfigSync } from 'cosmiconfig';
3
3
  import parseToml from '@iarna/toml/parse-string.js';
4
- import _ from 'lodash';
5
4
  import { isCI } from 'ci-info';
5
+ import defaultsDeep from '@nodeutils/defaults-deep';
6
+ import { isObjectStrict } from '@phun-ky/typeof';
7
+ import merge from 'lodash.merge';
8
+ import get from 'lodash.get';
6
9
  import { readJSON, getSystemInfo } from './util.js';
7
10
 
8
11
  const debug = util.debug('release-it:config');
@@ -35,7 +38,8 @@ const getLocalConfig = ({ file, dir = process.cwd() }) => {
35
38
  throw new Error(`Invalid configuration file at ${result.filepath}`);
36
39
  }
37
40
  debug({ cosmiconfig: result });
38
- return result && _.isPlainObject(result.config) ? result.config : localConfig;
41
+
42
+ return result && isObjectStrict(result.config) ? result.config : localConfig;
39
43
  };
40
44
 
41
45
  class Config {
@@ -72,7 +76,7 @@ class Config {
72
76
  }
73
77
 
74
78
  mergeOptions() {
75
- return _.defaultsDeep(
79
+ return defaultsDeep(
76
80
  {},
77
81
  this.constructorConfig,
78
82
  {
@@ -82,15 +86,14 @@ class Config {
82
86
  this.defaultConfig
83
87
  );
84
88
  }
85
-
86
89
  getContext(path) {
87
- const context = _.merge({}, this.options, this.contextOptions);
88
- return path ? _.get(context, path) : context;
90
+ const context = merge({}, this.options, this.contextOptions);
91
+ return path ? get(context, path) : context;
89
92
  }
90
93
 
91
94
  setContext(options) {
92
95
  debug(options);
93
- _.merge(this.contextOptions, options);
96
+ merge(this.contextOptions, options);
94
97
  }
95
98
 
96
99
  setCI(value = true) {
package/lib/index.js CHANGED
@@ -1,11 +1,10 @@
1
- import _ from 'lodash';
2
1
  import { getPlugins } from './plugin/factory.js';
3
2
  import Logger from './log.js';
4
3
  import Config from './config.js';
5
4
  import Shell from './shell.js';
6
5
  import Prompt from './prompt.js';
7
6
  import Spinner from './spinner.js';
8
- import { reduceUntil, parseVersion } from './util.js';
7
+ import { reduceUntil, parseVersion, castArray } from './util.js';
9
8
 
10
9
  const runTasks = async (opts, di) => {
11
10
  let container = {};
@@ -33,20 +32,20 @@ const runTasks = async (opts, di) => {
33
32
  if (!scripts || !scripts.length) return;
34
33
  const context = config.getContext();
35
34
  const external = true;
36
- for (const script of _.castArray(scripts)) {
35
+ for (const script of castArray(scripts)) {
37
36
  const task = () => shell.exec(script, { external }, context);
38
37
  await spinner.show({ task, label: script, context, external });
39
38
  }
40
39
  };
41
40
 
42
41
  const runLifeCycleHook = async (plugin, name, ...args) => {
43
- if (plugin === _.first(plugins)) await runHook('before', name);
42
+ if (plugin === plugins.at(0)) await runHook('before', name);
44
43
  await runHook('before', plugin.namespace, name);
45
44
  const willHookRun = (await plugin[name](...args)) !== false;
46
45
  if (willHookRun) {
47
46
  await runHook('after', plugin.namespace, name);
48
47
  }
49
- if (plugin === _.last(plugins)) await runHook('after', name);
48
+ if (plugin === plugins.at(-1)) await runHook('after', name);
50
49
  };
51
50
 
52
51
  const [internal, external] = await getPlugins(config, container);
package/lib/log.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import { EOL } from 'node:os';
2
2
  import chalk from 'chalk';
3
- import _ from 'lodash';
4
-
5
- const { isObject, last, filter, isString, lowerCase, upperFirst, isArray } = _;
3
+ import { isObjectLoose } from '@phun-ky/typeof';
4
+ import { upperFirst } from './util.js';
6
5
 
7
6
  class Logger {
8
7
  constructor({ isCI = true, isVerbose = false, verbosityLevel = 0, isDryRun = false } = {}) {
@@ -33,17 +32,19 @@ class Logger {
33
32
  }
34
33
 
35
34
  verbose(...args) {
36
- const { isExternal } = isObject(last(args)) ? last(args) : {};
35
+ const { isExternal } = isObjectLoose(args.at(-1)) ? args.at(-1) : {};
37
36
  if (this.shouldLog(isExternal)) {
38
- this.log(...filter(args, isString));
37
+ this.log(...args.filter(str => typeof str === 'string'));
39
38
  }
40
39
  }
41
40
 
42
41
  exec(...args) {
43
- const { isDryRun: isExecutedInDryRun, isExternal, isCached } = isObject(last(args)) ? last(args) : {};
42
+ const { isDryRun: isExecutedInDryRun, isExternal, isCached } = isObjectLoose(args.at(-1)) ? args.at(-1) : {};
44
43
  if (this.shouldLog(isExternal) || this.isDryRun) {
45
44
  const prefix = isExecutedInDryRun == null ? '$' : '!';
46
- const command = args.map(cmd => (isString(cmd) ? cmd : isArray(cmd) ? cmd.join(' ') : '')).join(' ');
45
+ const command = args
46
+ .map(cmd => (typeof cmd === 'string' ? cmd : Array.isArray(cmd) ? cmd.join(' ') : ''))
47
+ .join(' ');
47
48
  const message = [prefix, command, isCached ? '[cached]' : ''].join(' ').trim();
48
49
  this.log(message);
49
50
  }
@@ -61,7 +62,7 @@ class Logger {
61
62
  const body = text.replace(new RegExp(EOL + EOL, 'g'), EOL);
62
63
  this.obtrusive(`${header}:${EOL}${body}`);
63
64
  } else {
64
- this.obtrusive(`Empty ${lowerCase(title)}`);
65
+ this.obtrusive(`Empty ${title.toLowerCase()}`);
65
66
  }
66
67
  }
67
68
  }
@@ -22,7 +22,8 @@ class GitBase extends Plugin {
22
22
  }
23
23
 
24
24
  getName() {
25
- return this.getContext('repo.project');
25
+ const repo = this.getContext('repo');
26
+ return repo.project;
26
27
  }
27
28
 
28
29
  getLatestVersion() {
@@ -1,5 +1,4 @@
1
- import _ from 'lodash';
2
- import { readJSON } from '../util.js';
1
+ import { pick, readJSON } from '../util.js';
3
2
  import GitBase from './GitBase.js';
4
3
 
5
4
  const defaultConfig = readJSON(new URL('../../config/release-it.json', import.meta.url));
@@ -12,7 +11,7 @@ class GitRelease extends GitBase {
12
11
  getInitialOptions(options) {
13
12
  const baseOptions = super.getInitialOptions(...arguments);
14
13
  const git = options.git || defaultConfig.git;
15
- const gitOptions = _.pick(git, [
14
+ const gitOptions = pick(git, [
16
15
  'tagExclude',
17
16
  'tagName',
18
17
  'tagMatch',
@@ -21,12 +20,13 @@ class GitRelease extends GitBase {
21
20
  'changelog',
22
21
  'commit'
23
22
  ]);
24
- return _.defaults(baseOptions, gitOptions);
23
+
24
+ return Object.assign({}, gitOptions, baseOptions);
25
25
  }
26
26
 
27
27
  get token() {
28
28
  const { tokenRef } = this.options;
29
- return _.get(process.env, tokenRef, null);
29
+ return process.env[tokenRef] || null;
30
30
  }
31
31
 
32
32
  async beforeRelease() {
@@ -1,5 +1,6 @@
1
1
  import { debug } from 'node:util';
2
- import _ from 'lodash';
2
+ import merge from 'lodash.merge';
3
+ import get from 'lodash.get';
3
4
 
4
5
  class Plugin {
5
6
  static isEnabled() {
@@ -40,12 +41,13 @@ class Plugin {
40
41
  afterRelease() {}
41
42
 
42
43
  getContext(path) {
43
- const context = _.merge({}, this.options, this.context);
44
- return path ? _.get(context, path) : context;
44
+ const context = merge({}, this.options, this.context);
45
+
46
+ return path ? get(context, path) : context;
45
47
  }
46
48
 
47
49
  setContext(context) {
48
- _.merge(this.context, context);
50
+ merge(this.context, context);
49
51
  }
50
52
 
51
53
  exec(command, { options, context = {} } = {}) {
@@ -2,7 +2,7 @@ import url from 'node:url';
2
2
  import path from 'node:path';
3
3
  import util from 'node:util';
4
4
  import { createRequire } from 'node:module';
5
- import _ from 'lodash';
5
+ import { castArray } from '../util.js';
6
6
  import Version from './version/Version.js';
7
7
  import Git from './git/Git.js';
8
8
  import GitLab from './gitlab/GitLab.js';
@@ -54,23 +54,23 @@ export const getPluginName = pluginName => {
54
54
  export let getPlugins = async (config, container) => {
55
55
  const context = config.getContext();
56
56
  const disabledPlugins = [];
57
-
58
- const enabledExternalPlugins = await _.reduce(
59
- context.plugins,
60
- async (result, pluginConfig, pluginName) => {
57
+ const enabledExternalPlugins = [];
58
+ if (context.plugins) {
59
+ for (const [pluginName, pluginConfig] of Object.entries(context.plugins)) {
61
60
  const [name, Plugin] = await load(pluginName);
62
61
  const [namespace, options] = pluginConfig.length === 2 ? pluginConfig : [name, pluginConfig];
62
+
63
63
  config.setContext({ [namespace]: options });
64
+
64
65
  if (await Plugin.isEnabled(options)) {
65
66
  const instance = new Plugin({ namespace, options: config.getContext(), container });
66
67
  debug({ namespace, options: instance.options });
67
- (await result).push(instance);
68
- disabledPlugins.push(..._.intersection(pluginNames, _.castArray(Plugin.disablePlugin(options))));
68
+ enabledExternalPlugins.push(instance);
69
+
70
+ disabledPlugins.push(...pluginNames.filter(p => castArray(Plugin.disablePlugin(options)).includes(p)));
69
71
  }
70
- return result;
71
- },
72
- []
73
- );
72
+ }
73
+ }
74
74
 
75
75
  const enabledPlugins = await pluginNames.reduce(async (result, plugin) => {
76
76
  if (plugin in plugins && !disabledPlugins.includes(plugin)) {
@@ -1,15 +1,13 @@
1
1
  import { EOL } from 'node:os';
2
- import _ from 'lodash';
3
2
  import { execa } from 'execa';
4
3
  import matcher from 'wildcard-match';
5
- import { format, e } from '../../util.js';
4
+ import { format, e, fixArgs, once, castArray } from '../../util.js';
6
5
  import GitBase from '../GitBase.js';
7
6
  import prompts from './prompts.js';
8
7
 
9
8
  const noop = Promise.resolve();
10
9
  const invalidPushRepoRe = /^\S+@/;
11
10
  const options = { write: false };
12
- const fixArgs = args => (args ? (typeof args === 'string' ? args.split(' ') : args) : []);
13
11
 
14
12
  const docs = 'https://git.io/release-it-git';
15
13
 
@@ -65,7 +63,7 @@ class Git extends GitBase {
65
63
  }
66
64
 
67
65
  enableRollback() {
68
- this.rollbackOnce = _.once(this.rollback.bind(this));
66
+ this.rollbackOnce = once(this.rollback.bind(this));
69
67
  process.on('SIGINT', this.rollbackOnce);
70
68
  process.on('exit', this.rollbackOnce);
71
69
  }
@@ -97,7 +95,7 @@ class Git extends GitBase {
97
95
 
98
96
  async isRequiredBranch() {
99
97
  const branch = await this.getBranchName();
100
- const requiredBranches = _.castArray(this.options.requireBranch);
98
+ const requiredBranches = castArray(this.options.requireBranch);
101
99
  const [branches, negated] = requiredBranches.reduce(
102
100
  ([p, n], b) => (b.startsWith('!') ? [p, [...n, b.slice(1)]] : [[...p, b], n]),
103
101
  [[], []]
@@ -152,7 +150,7 @@ class Git extends GitBase {
152
150
 
153
151
  stage(file) {
154
152
  if (!file || !file.length) return noop;
155
- const files = _.castArray(file);
153
+ const files = castArray(file);
156
154
  return this.exec(['git', 'add', ...files]).catch(err => {
157
155
  this.log.warn(`Could not stage ${files}`);
158
156
  this.debug(err);
@@ -165,7 +163,7 @@ class Git extends GitBase {
165
163
  }
166
164
 
167
165
  reset(file) {
168
- const files = _.castArray(file);
166
+ const files = castArray(file);
169
167
  return this.exec(['git', 'checkout', 'HEAD', '--', ...files]).catch(err => {
170
168
  this.log.warn(`Could not reset ${files}`);
171
169
  this.debug(err);
@@ -1,14 +1,13 @@
1
- import fs from 'node:fs';
1
+ import { createReadStream, statSync } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import open from 'open';
4
4
  import { Octokit } from '@octokit/rest';
5
5
  import { globby } from 'globby';
6
6
  import mime from 'mime-types';
7
- import _ from 'lodash';
8
7
  import retry from 'async-retry';
9
8
  import newGithubReleaseUrl from 'new-github-release-url';
10
9
  import { ProxyAgent } from 'proxy-agent';
11
- import { format, parseVersion, readJSON, e } from '../../util.js';
10
+ import { format, parseVersion, readJSON, e, castArray } from '../../util.js';
12
11
  import Release from '../GitRelease.js';
13
12
  import prompts from './prompts.js';
14
13
  import { getCommitsFromChangelog, getResolvedIssuesFromChangelog, searchQueries } from './util.js';
@@ -34,7 +33,7 @@ const parseErrormsg = err => {
34
33
  if (err instanceof Error) {
35
34
  const { status, message } = err;
36
35
  const headers = err.response ? err.response.headers : {};
37
- msg = `${_.get(headers, 'status', status)} (${message})`;
36
+ msg = `${headers?.status || status} (${message})`;
38
37
  }
39
38
  return msg;
40
39
  };
@@ -166,7 +165,7 @@ class GitHub extends Release {
166
165
  const githubError = new Error(message);
167
166
  this.log.verbose(err.errors);
168
167
  this.debug(err);
169
- if (!_.includes(RETRY_CODES, err.status)) {
168
+ if (!RETRY_CODES.includes(err.status)) {
170
169
  return bail(githubError);
171
170
  }
172
171
  throw githubError;
@@ -175,7 +174,8 @@ class GitHub extends Release {
175
174
  get client() {
176
175
  if (this._client) return this._client;
177
176
  const { proxy, timeout } = this.options;
178
- const host = this.options.host || this.getContext('repo.host');
177
+ const repo = this.getContext('repo');
178
+ const host = this.options.host || repo.host;
179
179
  const isGitHub = host === 'github.com';
180
180
  const baseUrl = `https://${isGitHub ? 'api.github.com' : host}${isGitHub ? '' : '/api/v3'}`;
181
181
  const options = {
@@ -226,6 +226,7 @@ class GitHub extends Release {
226
226
  const { isPreRelease } = parseVersion(version);
227
227
  const name = format(releaseName, this.config.getContext());
228
228
  const releaseNotesObject = this.options.releaseNotes;
229
+
229
230
  const body = autoGenerate
230
231
  ? isUpdate
231
232
  ? null
@@ -301,13 +302,13 @@ class GitHub extends Release {
301
302
  const url = this.getContext('upload_url');
302
303
  const name = path.basename(filePath);
303
304
  const contentType = mime.contentType(name) || 'application/octet-stream';
304
- const contentLength = fs.statSync(filePath).size;
305
+ const contentLength = statSync(filePath).size;
305
306
 
306
307
  return this.retry(async bail => {
307
308
  try {
308
309
  const options = {
309
310
  url,
310
- data: fs.createReadStream(filePath),
311
+ data: createReadStream(filePath),
311
312
  name,
312
313
  headers: {
313
314
  'content-type': contentType,
@@ -331,7 +332,7 @@ class GitHub extends Release {
331
332
  const context = this.config.getContext();
332
333
  const { isDryRun } = this.config;
333
334
 
334
- const patterns = _.castArray(assets).map(pattern => format(pattern, context));
335
+ const patterns = castArray(assets).map(pattern => format(pattern, context));
335
336
 
336
337
  this.log.exec('octokit repos.uploadReleaseAssets', patterns, { isDryRun });
337
338
 
@@ -356,9 +357,9 @@ class GitHub extends Release {
356
357
  }
357
358
 
358
359
  async generateWebUrl() {
359
- const host = this.options.host || this.getContext('repo.host');
360
+ const repo = this.getContext('repo');
361
+ const host = this.options.host || repo.host;
360
362
  const isGitHub = host === 'github.com';
361
-
362
363
  const options = await this.getOctokitReleaseOptions();
363
364
  const url = newGithubReleaseUrl({
364
365
  user: options.owner,
@@ -422,13 +423,13 @@ class GitHub extends Release {
422
423
  const searchResults = await Promise.all(searchQueries(this.client, owner, repo, shas));
423
424
  const mergedPullRequests = searchResults.flatMap(items => items.map(item => ({ type: 'pr', number: item.number })));
424
425
 
425
- const host = 'https://' + (this.options.host || this.getContext('repo.host'));
426
- const resolvedIssues = getResolvedIssuesFromChangelog(host, owner, repo, changelog);
426
+ const hostURL = 'https://' + (this.options.host || repo.host);
427
+ const resolvedIssues = getResolvedIssuesFromChangelog(hostURL, owner, repo, changelog);
427
428
 
428
429
  for (const item of [...resolvedIssues, ...mergedPullRequests]) {
429
430
  const { type, number } = item;
430
431
  const comment = format(format(type === 'pr' ? pr : issue, context), context);
431
- const url = `${host}/${owner}/${repo}/${type === 'pr' ? 'pull' : 'issues'}/${number}`;
432
+ const url = `${hostURL}/${owner}/${repo}/${type === 'pr' ? 'pull' : 'issues'}/${number}`;
432
433
 
433
434
  if (isDryRun) {
434
435
  this.log.exec(`octokit issues.createComment (${url})`, { isDryRun });
@@ -1,10 +1,9 @@
1
- import fs from 'node:fs';
2
1
  import path from 'node:path';
2
+ import fs, { promises } from 'node:fs'; // import fs here so it can be stubbed in tests
3
3
  import { globby } from 'globby';
4
- import _ from 'lodash';
5
4
  import { Agent } from 'undici';
6
5
  import Release from '../GitRelease.js';
7
- import { format, e } from '../../util.js';
6
+ import { format, e, castArray } from '../../util.js';
8
7
  import prompts from './prompts.js';
9
8
 
10
9
  const docs = 'https://git.io/release-it-gitlab';
@@ -268,7 +267,7 @@ class GitLab extends Release {
268
267
  if (useGenericPackageRepositoryForAssets) {
269
268
  const options = {
270
269
  method: 'PUT',
271
- body: await fs.promises.readFile(filePath)
270
+ body: await promises.readFile(filePath)
272
271
  };
273
272
  try {
274
273
  const body = await this.request(endpoint, options);
@@ -286,7 +285,7 @@ class GitLab extends Release {
286
285
  }
287
286
  } else {
288
287
  const body = new FormData();
289
- const rawData = await fs.promises.readFile(filePath);
288
+ const rawData = await promises.readFile(filePath);
290
289
  body.set('file', new Blob([rawData]), name);
291
290
  const options = { body };
292
291
 
@@ -309,7 +308,7 @@ class GitLab extends Release {
309
308
  const { isDryRun } = this.config;
310
309
  const context = this.config.getContext();
311
310
 
312
- const patterns = _.castArray(assets).map(pattern => format(pattern, context));
311
+ const patterns = castArray(assets).map(pattern => format(pattern, context));
313
312
 
314
313
  this.log.exec('gitlab releases#uploadAssets', patterns, { isDryRun });
315
314
 
@@ -2,7 +2,7 @@ import path from 'node:path';
2
2
  import semver from 'semver';
3
3
  import urlJoin from 'url-join';
4
4
  import Plugin from '../Plugin.js';
5
- import { hasAccess, rejectAfter, parseVersion, readJSON, e } from '../../util.js';
5
+ import { hasAccess, rejectAfter, parseVersion, readJSON, e, fixArgs } from '../../util.js';
6
6
  import prompts from './prompts.js';
7
7
 
8
8
  const docs = 'https://git.io/release-it-npm';
@@ -15,8 +15,6 @@ const DEFAULT_TAG_PRERELEASE = 'next';
15
15
  const NPM_BASE_URL = 'https://www.npmjs.com';
16
16
  const NPM_PUBLIC_PATH = '/package';
17
17
 
18
- const fixArgs = args => (args ? (typeof args === 'string' ? args.split(' ') : args) : []);
19
-
20
18
  class npm extends Plugin {
21
19
  static isEnabled(options) {
22
20
  return hasAccess(MANIFEST_PATH) && options !== false;
package/lib/shell.js CHANGED
@@ -1,12 +1,10 @@
1
1
  import util from 'node:util';
2
- import sh from 'shelljs';
2
+ import childProcess from 'node:child_process';
3
3
  import { execa } from 'execa';
4
4
  import { format } from './util.js';
5
5
 
6
6
  const debug = util.debug('release-it:shell');
7
7
 
8
- sh.config.silent = !debug.enabled;
9
-
10
8
  const noop = Promise.resolve();
11
9
 
12
10
  class Shell {
@@ -55,8 +53,9 @@ class Shell {
55
53
 
56
54
  execStringCommand(command, options, { isExternal }) {
57
55
  return new Promise((resolve, reject) => {
58
- const childProcess = sh.exec(command, { async: true }, (code, stdout, stderr) => {
56
+ const proc = childProcess.exec(command, (err, stdout, stderr) => {
59
57
  stdout = stdout.toString().trimEnd();
58
+ const code = !err ? 0 : err === 'undefined' ? 1 : err.code;
60
59
  debug({ command, options, code, stdout, stderr });
61
60
  if (code === 0) {
62
61
  resolve(stdout);
@@ -64,8 +63,8 @@ class Shell {
64
63
  reject(new Error(stderr || stdout));
65
64
  }
66
65
  });
67
- childProcess.stdout.on('data', stdout => this.log.verbose(stdout.toString().trimEnd(), { isExternal }));
68
- childProcess.stderr.on('data', stderr => this.log.verbose(stderr.toString().trimEnd(), { isExternal }));
66
+ proc.stdout.on('data', stdout => this.log.verbose(stdout.toString().trimEnd(), { isExternal }));
67
+ proc.stderr.on('data', stderr => this.log.verbose(stderr.toString().trimEnd(), { isExternal }));
69
68
  });
70
69
  }
71
70