Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(esm): convert to esm (#2569)
for #2543

BREAKING CHANGE: semantic-release is now ESM-only. since it is used through its own executable, the impact on consuming projects should be minimal

BREAKING CHANGE: references to plugin files in configs need to include the file extension because of executing in an ESM context
  • Loading branch information
travi committed Nov 11, 2022
1 parent 4012f75 commit 9eab1ad
Show file tree
Hide file tree
Showing 67 changed files with 3,026 additions and 1,786 deletions.
28 changes: 15 additions & 13 deletions bin/semantic-release.js
@@ -1,20 +1,22 @@
#!/usr/bin/env node

// Bad news: We have to write plain ES5 in this file
// Good news: It's the only file of the entire project

/* eslint-disable no-var */

var semver = require('semver');
var execa = require('execa');
var findVersions = require('find-versions');
var pkg = require('../package.json');
import semver from 'semver';
import { execa } from 'execa';
import findVersions from 'find-versions';
import cli from '../cli.js';
import {createRequire} from 'node:module';

const require = createRequire(import.meta.url);
const { engines } = require('../package.json');
const { satisfies, lt } = semver;

var MIN_GIT_VERSION = '2.7.1';
const MIN_GIT_VERSION = '2.7.1';

if (!semver.satisfies(process.version, pkg.engines.node)) {
if (!satisfies(process.version, engines.node)) {
console.error(
`[semantic-release]: node version ${pkg.engines.node} is required. Found ${process.version}.
`[semantic-release]: node version ${engines.node} is required. Found ${process.version}.
See https://github.com/semantic-release/semantic-release/blob/master/docs/support/node-version.md for more details and solutions.`
);
Expand All @@ -23,8 +25,8 @@ See https://github.com/semantic-release/semantic-release/blob/master/docs/suppor

execa('git', ['--version'])
.then(({stdout}) => {
var gitVersion = findVersions(stdout)[0];
if (semver.lt(gitVersion, MIN_GIT_VERSION)) {
const gitVersion = findVersions(stdout)[0];
if (lt(gitVersion, MIN_GIT_VERSION)) {
console.error(`[semantic-release]: Git version ${MIN_GIT_VERSION} is required. Found ${gitVersion}.`);
process.exit(1);
}
Expand All @@ -36,7 +38,7 @@ execa('git', ['--version'])
});

// Node 10+ from this point on
require('../cli')()
cli()
.then((exitCode) => {
process.exitCode = exitCode;
})
Expand Down
22 changes: 11 additions & 11 deletions cli.js
@@ -1,6 +1,7 @@
const {argv, env, stderr} = require('process'); // eslint-disable-line node/prefer-global/process
const util = require('util');
const hideSensitive = require('./lib/hide-sensitive');
import util from 'node:util';
import yargs from 'yargs';
import {hideBin} from 'yargs/helpers';
import hideSensitive from './lib/hide-sensitive.js';

const stringList = {
type: 'string',
Expand All @@ -11,8 +12,8 @@ const stringList = {
: values.reduce((values, value) => values.concat(value.split(',').map((value) => value.trim())), []),
};

module.exports = async () => {
const cli = require('yargs')
export default async () => {
const cli = yargs(hideBin(process.argv))
.command('$0', 'Run automated package publishing', (yargs) => {
yargs.demandCommand(0, 0).usage(`Run automated package publishing
Expand All @@ -36,29 +37,28 @@ Usage:
.option('debug', {describe: 'Output debugging information', type: 'boolean', group: 'Options'})
.option('d', {alias: 'dry-run', describe: 'Skip publishing', type: 'boolean', group: 'Options'})
.option('h', {alias: 'help', group: 'Options'})
.option('v', {alias: 'version', group: 'Options'})
.strict(false)
.exitProcess(false);

try {
const {help, version, ...options} = cli.parse(argv.slice(2));
const {help, version, ...options} = cli.parse(process.argv.slice(2));

if (Boolean(help) || Boolean(version)) {
return 0;
}

if (options.debug) {
// Debug must be enabled before other requires in order to work
require('debug').enable('semantic-release:*');
(await import('debug')).default.enable('semantic-release:*');
}

await require('.')(options);
await (await import('./index.js')).default(options);
return 0;
} catch (error) {
if (error.name !== 'YError') {
stderr.write(hideSensitive(env)(util.inspect(error, {colors: true})));
process.stderr.write(hideSensitive(process.env)(util.inspect(error, {colors: true})));
}

return 1;
}
};
}
51 changes: 27 additions & 24 deletions index.js
@@ -1,24 +1,27 @@
const {pick} = require('lodash');
const marked = require('marked');
const envCi = require('env-ci');
const hookStd = require('hook-std');
const semver = require('semver');
const AggregateError = require('aggregate-error');
import {createRequire} from 'node:module';
import {pick} from 'lodash-es';
import * as marked from 'marked';
import envCi from 'env-ci';
import {hookStdout} from 'hook-std';
import semver from 'semver';
import AggregateError from 'aggregate-error';
import hideSensitive from './lib/hide-sensitive.js';
import getConfig from './lib/get-config.js';
import verify from './lib/verify.js';
import getNextVersion from './lib/get-next-version.js';
import getCommits from './lib/get-commits.js';
import getLastRelease from './lib/get-last-release.js';
import getReleaseToAdd from './lib/get-release-to-add.js';
import {extractErrors, makeTag} from './lib/utils.js';
import getGitAuthUrl from './lib/get-git-auth-url.js';
import getBranches from './lib/branches/index.js';
import getLogger from './lib/get-logger.js';
import {addNote, getGitHead, getTagHead, isBranchUpToDate, push, pushNotes, tag, verifyAuth} from './lib/git.js';
import getError from './lib/get-error.js';
import {COMMIT_EMAIL, COMMIT_NAME} from './lib/definitions/constants.js';

const require = createRequire(import.meta.url);
const pkg = require('./package.json');
const hideSensitive = require('./lib/hide-sensitive');
const getConfig = require('./lib/get-config');
const verify = require('./lib/verify');
const getNextVersion = require('./lib/get-next-version');
const getCommits = require('./lib/get-commits');
const getLastRelease = require('./lib/get-last-release');
const getReleaseToAdd = require('./lib/get-release-to-add');
const {extractErrors, makeTag} = require('./lib/utils');
const getGitAuthUrl = require('./lib/get-git-auth-url');
const getBranches = require('./lib/branches');
const getLogger = require('./lib/get-logger');
const {verifyAuth, isBranchUpToDate, getGitHead, tag, push, pushNotes, getTagHead, addNote} = require('./lib/git');
const getError = require('./lib/get-error');
const {COMMIT_NAME, COMMIT_EMAIL} = require('./lib/definitions/constants');

let markedOptionsSet = false;
async function terminalOutput(text) {
Expand All @@ -41,7 +44,7 @@ async function run(context, plugins) {
logger.warn('This run was not triggered in a known CI environment, running in dry-run mode.');
options.dryRun = true;
} else {
// When running on CI, set the commits author and commiter info and prevent the `git` CLI to prompt for username/password. See #703.
// When running on CI, set the commits author and committer info and prevent the `git` CLI to prompt for username/password. See #703.
Object.assign(env, {
GIT_AUTHOR_NAME: COMMIT_NAME,
GIT_AUTHOR_EMAIL: COMMIT_EMAIL,
Expand Down Expand Up @@ -247,8 +250,8 @@ async function callFail(context, plugins, err) {
}
}

module.exports = async (cliOptions = {}, {cwd = process.cwd(), env = process.env, stdout, stderr} = {}) => {
const {unhook} = hookStd(
export default async (cliOptions = {}, {cwd = process.cwd(), env = process.env, stdout, stderr} = {}) => {
const {unhook} = hookStdout(
{silent: false, streams: [process.stdout, process.stderr, stdout, stderr].filter(Boolean)},
hideSensitive(env)
);
Expand Down Expand Up @@ -278,4 +281,4 @@ module.exports = async (cliOptions = {}, {cwd = process.cwd(), env = process.env
unhook();
throw error;
}
};
}
10 changes: 5 additions & 5 deletions lib/branches/expand.js
@@ -1,8 +1,8 @@
const {isString, remove, omit, mapValues, template} = require('lodash');
const micromatch = require('micromatch');
const {getBranches} = require('../git');
import {isString, mapValues, omit, remove, template} from 'lodash-es';
import micromatch from 'micromatch';
import {getBranches} from '../git.js';

module.exports = async (repositoryUrl, {cwd}, branches) => {
export default async (repositoryUrl, {cwd}, branches) => {
const gitBranches = await getBranches(repositoryUrl, {cwd});

return branches.reduce(
Expand All @@ -15,4 +15,4 @@ module.exports = async (repositoryUrl, {cwd}, branches) => {
],
[]
);
};
}
17 changes: 10 additions & 7 deletions lib/branches/get-tags.js
@@ -1,10 +1,13 @@
const {template, escapeRegExp} = require('lodash');
const semver = require('semver');
const pReduce = require('p-reduce');
const debug = require('debug')('semantic-release:get-tags');
const {getTags, getNote} = require('../../lib/git');
import {escapeRegExp, template} from 'lodash-es';
import semver from 'semver';
import pReduce from 'p-reduce';
import debugTags from 'debug';
import {getNote, getTags} from '../../lib/git.js';

module.exports = async ({cwd, env, options: {tagFormat}}, branches) => {
const debug = debugTags('semantic-release:get-tags');


export default async ({cwd, env, options: {tagFormat}}, branches) => {
// Generate a regex to parse tags formatted with `tagFormat`
// by replacing the `version` variable in the template by `(.+)`.
// The `tagFormat` is compiled with space as the `version` as it's an invalid tag character,
Expand All @@ -30,4 +33,4 @@ module.exports = async ({cwd, env, options: {tagFormat}}, branches) => {
},
[]
);
};
}
22 changes: 11 additions & 11 deletions lib/branches/index.js
@@ -1,14 +1,14 @@
const {isString, isRegExp} = require('lodash');
const AggregateError = require('aggregate-error');
const pEachSeries = require('p-each-series');
const DEFINITIONS = require('../definitions/branches');
const getError = require('../get-error');
const {fetch, fetchNotes, verifyBranchName} = require('../git');
const expand = require('./expand');
const getTags = require('./get-tags');
const normalize = require('./normalize');
import {isRegExp, isString} from 'lodash-es';
import AggregateError from 'aggregate-error';
import pEachSeries from 'p-each-series';
import * as DEFINITIONS from '../definitions/branches.js';
import getError from '../get-error.js';
import {fetch, fetchNotes, verifyBranchName} from '../git.js';
import expand from './expand.js';
import getTags from './get-tags.js';
import * as normalize from './normalize.js';

module.exports = async (repositoryUrl, ciBranch, context) => {
export default async (repositoryUrl, ciBranch, context) => {
const {cwd, env} = context;

const remoteBranches = await expand(
Expand Down Expand Up @@ -68,4 +68,4 @@ module.exports = async (repositoryUrl, ciBranch, context) => {
}

return [...result.maintenance, ...result.release, ...result.prerelease];
};
}
29 changes: 13 additions & 16 deletions lib/branches/normalize.js
@@ -1,19 +1,18 @@
const {sortBy, isNil} = require('lodash');
const semverDiff = require('semver-diff');
const {FIRST_RELEASE, RELEASE_TYPE} = require('../definitions/constants');
const {
tagsToVersions,
isMajorRange,
import {isNil, sortBy} from 'lodash-es';
import semverDiff from 'semver-diff';
import {FIRST_RELEASE, RELEASE_TYPE} from '../definitions/constants.js';
import {
getFirstVersion,
getLatestVersion,
getLowerBound, getRange,
getUpperBound,
getLowerBound,
highest,
isMajorRange,
lowest,
getLatestVersion,
getFirstVersion,
getRange,
} = require('../utils');
tagsToVersions
} from '../utils.js';

function maintenance({maintenance, release}) {
export function maintenance({maintenance, release}) {
return sortBy(
maintenance.map(({name, range, channel, ...rest}) => ({
...rest,
Expand Down Expand Up @@ -55,7 +54,7 @@ function maintenance({maintenance, release}) {
});
}

function release({release}) {
export function release({release}) {
if (release.length === 0) {
return release;
}
Expand Down Expand Up @@ -89,7 +88,7 @@ function release({release}) {
});
}

function prerelease({prerelease}) {
export function prerelease({prerelease}) {
return prerelease.map(({name, prerelease, channel, tags, ...rest}) => {
const preid = prerelease === true ? name : prerelease;
return {
Expand All @@ -102,5 +101,3 @@ function prerelease({prerelease}) {
};
});
}

module.exports = {maintenance, release, prerelease};
14 changes: 6 additions & 8 deletions lib/definitions/branches.js
@@ -1,24 +1,22 @@
const {isNil, uniqBy} = require('lodash');
const semver = require('semver');
const {isMaintenanceRange} = require('../utils');
import {isNil, uniqBy} from 'lodash-es';
import semver from 'semver';
import {isMaintenanceRange} from '../utils.js';

const maintenance = {
export const maintenance = {
filter: ({name, range}) => (!isNil(range) && range !== false) || isMaintenanceRange(name),
branchValidator: ({range}) => (isNil(range) ? true : isMaintenanceRange(range)),
branchesValidator: (branches) => uniqBy(branches, ({range}) => semver.validRange(range)).length === branches.length,
};

const prerelease = {
export const prerelease = {
filter: ({prerelease}) => !isNil(prerelease) && prerelease !== false,
branchValidator: ({name, prerelease}) =>
Boolean(prerelease) && Boolean(semver.valid(`1.0.0-${prerelease === true ? name : prerelease}.1`)),
branchesValidator: (branches) => uniqBy(branches, 'prerelease').length === branches.length,
};

const release = {
export const release = {
// eslint-disable-next-line unicorn/no-fn-reference-in-iterator
filter: (branch) => !maintenance.filter(branch) && !prerelease.filter(branch),
branchesValidator: (branches) => branches.length <= 3 && branches.length > 0,
};

module.exports = {maintenance, prerelease, release};
30 changes: 9 additions & 21 deletions lib/definitions/constants.js
@@ -1,29 +1,17 @@
const RELEASE_TYPE = ['patch', 'minor', 'major'];
export const RELEASE_TYPE = ['patch', 'minor', 'major'];

const FIRST_RELEASE = '1.0.0';
export const FIRST_RELEASE = '1.0.0';

const FIRSTPRERELEASE = '1';
export const FIRSTPRERELEASE = '1';

const COMMIT_NAME = 'semantic-release-bot';
export const COMMIT_NAME = 'semantic-release-bot';

const COMMIT_EMAIL = 'semantic-release-bot@martynus.net';
export const COMMIT_EMAIL = 'semantic-release-bot@martynus.net';

const RELEASE_NOTES_SEPARATOR = '\n\n';
export const RELEASE_NOTES_SEPARATOR = '\n\n';

const SECRET_REPLACEMENT = '[secure]';
export const SECRET_REPLACEMENT = '[secure]';

const SECRET_MIN_SIZE = 5;
export const SECRET_MIN_SIZE = 5;

const GIT_NOTE_REF = 'semantic-release';

module.exports = {
RELEASE_TYPE,
FIRST_RELEASE,
FIRSTPRERELEASE,
COMMIT_NAME,
COMMIT_EMAIL,
RELEASE_NOTES_SEPARATOR,
SECRET_REPLACEMENT,
SECRET_MIN_SIZE,
GIT_NOTE_REF,
};
export const GIT_NOTE_REF = 'semantic-release';

0 comments on commit 9eab1ad

Please sign in to comment.