Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for ESM presets #537

Merged
merged 6 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ const debug = debugFactory("semantic-release:commit-analyzer");
* @param {Array<Object>} context.commits The commits to analyze.
* @param {String} context.cwd The current working directory.
*
* @returns {String|null} the type of release to create based on the list of commits or `null` if no release has to be done.
* @returns {Promise<String|null>} the type of release to create based on the list of commits or `null` if no release has to be done.
*/
export async function analyzeCommits(pluginConfig, context) {
const { commits, logger } = context;
const releaseRules = loadReleaseRules(pluginConfig, context);
const releaseRules = await loadReleaseRules(pluginConfig, context);
const config = await loadParserConfig(pluginConfig, context);
let releaseType = null;

Expand Down
11 changes: 6 additions & 5 deletions lib/load-parser-config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { isPlainObject } from "lodash-es";
import importFrom from "import-from";
import importFrom from "import-from-esm";
import conventionalChangelogAngular from "conventional-changelog-angular";

/**
Expand All @@ -14,6 +12,7 @@ import conventionalChangelogAngular from "conventional-changelog-angular";
* @param {Object} pluginConfig.parserOpts Additional `conventional-changelog-parser` options that will overwrite ones loaded by `preset` or `config`.
* @param {Object} context The semantic-release context.
* @param {String} context.cwd The current working directory.
*
* @return {Promise<Object>} a `Promise` that resolve to the `conventional-changelog-parser` options.
*/
export default async ({ preset, config, parserOpts, presetConfig }, { cwd }) => {
Expand All @@ -22,9 +21,11 @@ export default async ({ preset, config, parserOpts, presetConfig }, { cwd }) =>

if (preset) {
const presetPackage = `conventional-changelog-${preset.toLowerCase()}`;
loadedConfig = await (importFrom.silent(__dirname, presetPackage) || importFrom(cwd, presetPackage))(presetConfig);
loadedConfig = await (
(await importFrom.silent(__dirname, presetPackage)) || (await importFrom(cwd, presetPackage))
)(presetConfig);
} else if (config) {
loadedConfig = await (importFrom.silent(__dirname, config) || importFrom(cwd, config))();
loadedConfig = await ((await importFrom.silent(__dirname, config)) || (await importFrom(cwd, config)))();
} else {
loadedConfig = await conventionalChangelogAngular();
}
Expand Down
8 changes: 4 additions & 4 deletions lib/load-release-rules.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { isUndefined } from "lodash-es";
import importFrom from "import-from";
import importFrom from "import-from-esm";
import RELEASE_TYPES from "./default-release-types.js";

/**
Expand All @@ -15,16 +15,16 @@ import RELEASE_TYPES from "./default-release-types.js";
* @param {Object} context The semantic-release context.
* @param {String} context.cwd The current working directory.
*
* @return {Array} the loaded and validated `releaseRules`.
* @return {Promise<Array>} the loaded and validated `releaseRules`.
*/
export default ({ releaseRules }, { cwd }) => {
export default async ({ releaseRules }, { cwd }) => {
let loadedReleaseRules;
const __dirname = dirname(fileURLToPath(import.meta.url));

if (releaseRules) {
loadedReleaseRules =
typeof releaseRules === "string"
? importFrom.silent(__dirname, releaseRules) || importFrom(cwd, releaseRules)
? (await importFrom.silent(__dirname, releaseRules)) || (await importFrom(cwd, releaseRules))
: releaseRules;

if (!Array.isArray(loadedReleaseRules)) {
Expand Down
11 changes: 10 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"conventional-commits-filter": "^4.0.0",
"conventional-commits-parser": "^5.0.0",
"debug": "^4.0.0",
"import-from": "^4.0.0",
"import-from-esm": "^1.0.3",
"lodash-es": "^4.17.21",
"micromatch": "^4.0.2"
},
Expand Down
11 changes: 11 additions & 0 deletions test/load-parser-config.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import test from "ava";
import importFrom from "import-from-esm";
import sinon from "sinon";
import loadParserConfig from "../lib/load-parser-config.js";

const cwd = process.cwd();
Expand Down Expand Up @@ -100,3 +102,12 @@ test('Throw error if "config" doesn`t exist', async (t) => {
test('Throw error if "preset" doesn`t exist', async (t) => {
await t.throwsAsync(loadParserConfig({ preset: "unknown-preset" }, { cwd }), { code: "MODULE_NOT_FOUND" });
});

test.serial("Load preset and config correctly when importFrom.silent fails", async (t) => {
sinon.stub(importFrom, "silent").returns(undefined);
sheerlox marked this conversation as resolved.
Show resolved Hide resolved

await loadPreset(t, "angular");
await loadConfig(t, "angular");
sheerlox marked this conversation as resolved.
Show resolved Hide resolved

sinon.restore();
});
36 changes: 18 additions & 18 deletions test/load-release-rules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,26 @@ import testReleaseRules from "./fixtures/release-rules.cjs";

const cwd = process.cwd();

test('Accept a "releaseRules" option', (t) => {
const releaseRules = loadReleaseRules({ releaseRules: testReleaseRules }, { cwd });
test('Accept a "releaseRules" option', async (t) => {
const releaseRules = await loadReleaseRules({ releaseRules: testReleaseRules }, { cwd });

t.deepEqual(releaseRules, testReleaseRules);
});

test('Accept a "releaseRules" option that reference a requireable module', (t) => {
const releaseRules = loadReleaseRules({ releaseRules: "./test/fixtures/release-rules.cjs" }, { cwd });
test('Accept a "releaseRules" option that reference a requireable module', async (t) => {
const releaseRules = await loadReleaseRules({ releaseRules: "./test/fixtures/release-rules.cjs" }, { cwd });

t.deepEqual(releaseRules, testReleaseRules);
});

test('Return undefined if "releaseRules" not set', (t) => {
const releaseRules = loadReleaseRules({}, { cwd });
test('Return undefined if "releaseRules" not set', async (t) => {
const releaseRules = await loadReleaseRules({}, { cwd });

t.is(releaseRules, undefined);
});

test('Preserve release rules set to "false" or "null"', (t) => {
const releaseRules = loadReleaseRules(
test('Preserve release rules set to "false" or "null"', async (t) => {
const releaseRules = await loadReleaseRules(
{
releaseRules: [
{ type: "feat", release: false },
Expand All @@ -39,32 +39,32 @@ test('Preserve release rules set to "false" or "null"', (t) => {
]);
});

test('Throw error if "releaseRules" reference invalid commit type', (t) => {
t.throws(() => loadReleaseRules({ releaseRules: [{ tag: "Update", release: "invalid" }] }, { cwd }), {
test('Throw error if "releaseRules" reference invalid commit type', async (t) => {
await t.throwsAsync(loadReleaseRules({ releaseRules: [{ tag: "Update", release: "invalid" }] }, { cwd }), {
message: /Error in commit-analyzer configuration: "invalid" is not a valid release type\. Valid values are:\[?.*]/,
});
});

test('Throw error if a rule in "releaseRules" does not have a release type', (t) => {
t.throws(() => loadReleaseRules({ releaseRules: [{ tag: "Update" }] }, { cwd }), {
test('Throw error if a rule in "releaseRules" does not have a release type', async (t) => {
await t.throwsAsync(loadReleaseRules({ releaseRules: [{ tag: "Update" }] }, { cwd }), {
message: /Error in commit-analyzer configuration: rules must be an object with a "release" property/,
});
});

test('Throw error if "releaseRules" is not an Array or a String', (t) => {
t.throws(() => loadReleaseRules({ releaseRules: {} }, { cwd }), {
test('Throw error if "releaseRules" is not an Array or a String', async (t) => {
await t.throwsAsync(loadReleaseRules({ releaseRules: {} }, { cwd }), {
message: /Error in commit-analyzer configuration: "releaseRules" must be an array of rules/,
});
});

test('Throw error if "releaseRules" option reference a requirable module that is not an Array or a String', (t) => {
t.throws(() => loadReleaseRules({ releaseRules: "./test/fixtures/release-rules-invalid.cjs" }, { cwd }), {
test('Throw error if "releaseRules" option reference a requirable module that is not an Array or a String', async (t) => {
await t.throwsAsync(loadReleaseRules({ releaseRules: "./test/fixtures/release-rules-invalid.cjs" }, { cwd }), {
message: /Error in commit-analyzer configuration: "releaseRules" must be an array of rules/,
});
});

test('Throw error if "releaseRules" contains an undefined rule', (t) => {
t.throws(() => loadReleaseRules({ releaseRules: [{ type: "feat", release: "minor" }, undefined] }, { cwd }), {
test('Throw error if "releaseRules" contains an undefined rule', async (t) => {
await t.throwsAsync(loadReleaseRules({ releaseRules: [{ type: "feat", release: "minor" }, undefined] }, { cwd }), {
message: /Error in commit-analyzer configuration: rules must be an object with a "release" property/,
});
});