Skip to content

Commit 323c1fd

Browse files
committed
feat: add support for ESM presets
1 parent 04f5708 commit 323c1fd

7 files changed

+53
-40
lines changed

index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ const debug = debugFactory("semantic-release:commit-analyzer");
2323
* @param {Array<Object>} context.commits The commits to analyze.
2424
* @param {String} context.cwd The current working directory.
2525
*
26-
* @returns {String|null} the type of release to create based on the list of commits or `null` if no release has to be done.
26+
* @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.
2727
*/
2828
export async function analyzeCommits(pluginConfig, context) {
2929
const { commits, logger } = context;
30-
const releaseRules = loadReleaseRules(pluginConfig, context);
30+
const releaseRules = await loadReleaseRules(pluginConfig, context);
3131
const config = await loadParserConfig(pluginConfig, context);
3232
let releaseType = null;
3333

lib/load-parser-config.js

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
import { dirname } from "node:path";
2-
import { fileURLToPath } from "node:url";
3-
import { promisify } from "node:util";
4-
import { isPlainObject } from "lodash-es";
5-
import importFrom from "import-from";
61
import conventionalChangelogAngular from "conventional-changelog-angular";
2+
import { importModule } from "./module-loader.js";
73

84
/**
95
* Load `conventional-changelog-parser` options. Handle presets that return either a `Promise<Array>` or a `Promise<Function>`.
@@ -14,17 +10,17 @@ import conventionalChangelogAngular from "conventional-changelog-angular";
1410
* @param {Object} pluginConfig.parserOpts Additional `conventional-changelog-parser` options that will overwrite ones loaded by `preset` or `config`.
1511
* @param {Object} context The semantic-release context.
1612
* @param {String} context.cwd The current working directory.
13+
*
1714
* @return {Promise<Object>} a `Promise` that resolve to the `conventional-changelog-parser` options.
1815
*/
1916
export default async ({ preset, config, parserOpts, presetConfig }, { cwd }) => {
2017
let loadedConfig;
21-
const __dirname = dirname(fileURLToPath(import.meta.url));
2218

2319
if (preset) {
2420
const presetPackage = `conventional-changelog-${preset.toLowerCase()}`;
25-
loadedConfig = await (importFrom.silent(__dirname, presetPackage) || importFrom(cwd, presetPackage))(presetConfig);
21+
loadedConfig = await (await importModule(cwd, presetPackage))(presetConfig);
2622
} else if (config) {
27-
loadedConfig = await (importFrom.silent(__dirname, config) || importFrom(cwd, config))();
23+
loadedConfig = await (await importModule(cwd, config))();
2824
} else {
2925
loadedConfig = await conventionalChangelogAngular();
3026
}

lib/load-release-rules.js

+4-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { dirname } from "node:path";
2-
import { fileURLToPath } from "node:url";
31
import { isUndefined } from "lodash-es";
4-
import importFrom from "import-from";
52
import RELEASE_TYPES from "./default-release-types.js";
3+
import { importModule } from "./module-loader.js";
64

75
/**
86
* Load and validate the `releaseRules` rules.
@@ -15,17 +13,13 @@ import RELEASE_TYPES from "./default-release-types.js";
1513
* @param {Object} context The semantic-release context.
1614
* @param {String} context.cwd The current working directory.
1715
*
18-
* @return {Array} the loaded and validated `releaseRules`.
16+
* @return {Promise<Array>} the loaded and validated `releaseRules`.
1917
*/
20-
export default ({ releaseRules }, { cwd }) => {
18+
export default async ({ releaseRules }, { cwd }) => {
2119
let loadedReleaseRules;
22-
const __dirname = dirname(fileURLToPath(import.meta.url));
2320

2421
if (releaseRules) {
25-
loadedReleaseRules =
26-
typeof releaseRules === "string"
27-
? importFrom.silent(__dirname, releaseRules) || importFrom(cwd, releaseRules)
28-
: releaseRules;
22+
loadedReleaseRules = typeof releaseRules === "string" ? await importModule(cwd, releaseRules) : releaseRules;
2923

3024
if (!Array.isArray(loadedReleaseRules)) {
3125
throw new TypeError('Error in commit-analyzer configuration: "releaseRules" must be an array of rules');

lib/module-loader.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { join } from "node:path";
2+
3+
/**
4+
* Import a module from node_modules or current working directory.
5+
*
6+
* @param {string} cwd the current working directory.
7+
* @param {string} moduleName npm package name or path relative to cwd.
8+
*
9+
* @return {Promise<any>} the loaded module's default export.
10+
*/
11+
export const importModule = async (cwd, moduleName) => {
12+
const localModulePath = join(cwd, moduleName);
13+
try {
14+
return (await import(moduleName)).default;
15+
} catch (e) {
16+
try {
17+
return (await import(localModulePath)).default;
18+
} catch (e) {
19+
const error = new Error(`Cannot find module "${moduleName}" or "${localModulePath}".`);
20+
error.code = "MODULE_NOT_FOUND";
21+
throw error;
22+
}
23+
}
24+
};

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
"conventional-commits-filter": "^4.0.0",
2222
"conventional-commits-parser": "^5.0.0",
2323
"debug": "^4.0.0",
24-
"import-from": "^4.0.0",
2524
"lodash-es": "^4.17.21",
2625
"micromatch": "^4.0.2"
2726
},

test/load-release-rules.test.js

+18-18
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,26 @@ import testReleaseRules from "./fixtures/release-rules.cjs";
44

55
const cwd = process.cwd();
66

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

1010
t.deepEqual(releaseRules, testReleaseRules);
1111
});
1212

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

1616
t.deepEqual(releaseRules, testReleaseRules);
1717
});
1818

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

2222
t.is(releaseRules, undefined);
2323
});
2424

25-
test('Preserve release rules set to "false" or "null"', (t) => {
26-
const releaseRules = loadReleaseRules(
25+
test('Preserve release rules set to "false" or "null"', async (t) => {
26+
const releaseRules = await loadReleaseRules(
2727
{
2828
releaseRules: [
2929
{ type: "feat", release: false },
@@ -39,32 +39,32 @@ test('Preserve release rules set to "false" or "null"', (t) => {
3939
]);
4040
});
4141

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

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

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

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

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

0 commit comments

Comments
 (0)