diff --git a/@commitlint/cli/fixtures/custom-formatter/formatters/custom.js b/@commitlint/cli/fixtures/custom-formatter/formatters/custom.js new file mode 100644 index 0000000000..96b56d55e4 --- /dev/null +++ b/@commitlint/cli/fixtures/custom-formatter/formatters/custom.js @@ -0,0 +1,3 @@ +module.exports = function (report) { + return 'custom-formatter-ok'; +} diff --git a/@commitlint/cli/package.json b/@commitlint/cli/package.json index a14eadc457..6a5ad310c9 100644 --- a/@commitlint/cli/package.json +++ b/@commitlint/cli/package.json @@ -84,6 +84,8 @@ "get-stdin": "5.0.1", "lodash.merge": "4.6.1", "lodash.pick": "4.4.0", - "meow": "5.0.0" + "meow": "5.0.0", + "resolve-from": "^4.0.0", + "resolve-global": "^0.1.0" } } diff --git a/@commitlint/cli/src/cli.js b/@commitlint/cli/src/cli.js index 55607fc82b..a82724f2b3 100755 --- a/@commitlint/cli/src/cli.js +++ b/@commitlint/cli/src/cli.js @@ -8,6 +8,8 @@ const meow = require('meow'); const merge = require('lodash.merge'); const pick = require('lodash.pick'); const stdin = require('get-stdin'); +const resolveFrom = require('resolve-from'); +const resolveGlobal = require('resolve-global'); const pkg = require('../package'); const help = require('./help'); @@ -262,15 +264,16 @@ function selectParserOpts(parserPreset) { function loadFormatter(config, flags) { const moduleName = flags.format || config.formatter; - let modulePath; + const modulePath = + resolveFrom.silent(__dirname, moduleName) || + resolveFrom.silent(flags.cwd, moduleName) || + resolveGlobal.silent(moduleName); - try { - modulePath = require.resolve(`${moduleName}`); - } catch (error) { - throw new Error(`Using format ${moduleName}, but cannot find the module.`); + if (modulePath) { + return require(modulePath); } - return require(modulePath); + throw new Error(`Using format ${moduleName}, but cannot find the module.`); } // Catch unhandled rejections globally diff --git a/@commitlint/cli/src/cli.test.js b/@commitlint/cli/src/cli.test.js index 3c5eb36cbb..94de3faf65 100644 --- a/@commitlint/cli/src/cli.test.js +++ b/@commitlint/cli/src/cli.test.js @@ -263,6 +263,23 @@ test('should fail for invalid formatters from flags', async t => { t.is(actual.code, 1); }); +test('should work with absolute formatter path', async t => { + const formatterPath = path.resolve(__dirname, '../fixtures/custom-formatter/formatters/custom.js'); + const cwd = await git.bootstrap('fixtures/custom-formatter'); + const actual = await cli(['--format', formatterPath], {cwd})('test: this should work'); + + t.true(actual.stdout.includes('custom-formatter-ok')); + t.is(actual.code, 0); +}); + +test('should work with relative formatter path', async t => { + const cwd = path.resolve(await git.bootstrap('fixtures/custom-formatter'), './formatters'); + const actual = await cli(['--format', './custom.js'], {cwd})('test: this should work'); + + t.true(actual.stdout.includes('custom-formatter-ok')); + t.is(actual.code, 0); +}); + async function writePkg(payload, options) { const pkgPath = path.join(options.cwd, 'package.json'); const pkg = JSON.parse(await sander.readFile(pkgPath)); diff --git a/@commitlint/load/fixtures/formatter-local-module/commitlint.config.js b/@commitlint/load/fixtures/formatter-local-module/commitlint.config.js new file mode 100644 index 0000000000..1d62bb6ee7 --- /dev/null +++ b/@commitlint/load/fixtures/formatter-local-module/commitlint.config.js @@ -0,0 +1,3 @@ +module.exports = { + formatter: './formatters/custom.js' +}; diff --git a/@commitlint/load/fixtures/formatter-local-module/formatters/custom.js b/@commitlint/load/fixtures/formatter-local-module/formatters/custom.js new file mode 100644 index 0000000000..7fb04a7d0c --- /dev/null +++ b/@commitlint/load/fixtures/formatter-local-module/formatters/custom.js @@ -0,0 +1,3 @@ +module.exports = function (report) { + return 'ok'; +} diff --git a/@commitlint/load/src/index.js b/@commitlint/load/src/index.js index 2f13f54d51..f7b900f9ca 100644 --- a/@commitlint/load/src/index.js +++ b/@commitlint/load/src/index.js @@ -52,6 +52,11 @@ export default async (seed = {}, options = {cwd: process.cwd()}) => { .parserOpts).parserOpts; } + // Resolve config-relative formatter module + if (typeof config.formatter === 'string') { + preset.formatter = resolveFrom.silent(base, config.formatter) || config.formatter; + } + // Execute rule config functions if needed const executed = await Promise.all( ['rules'] diff --git a/@commitlint/load/src/index.test.js b/@commitlint/load/src/index.test.js index 052d42a2e1..854fa0f3b2 100644 --- a/@commitlint/load/src/index.test.js +++ b/@commitlint/load/src/index.test.js @@ -1,6 +1,7 @@ import path from 'path'; import {fix, git} from '@commitlint/test'; import test from 'ava'; +import resolveFrom from 'resolve-from'; import load from '.'; @@ -233,3 +234,25 @@ test('respects formatter option', async t => { rules: {} }); }); + +test('resolves formatter relative from config directory', async t => { + const cwd = await git.bootstrap('fixtures/formatter-local-module'); + const actual = await load({}, {cwd}); + + t.deepEqual(actual, { + formatter: resolveFrom(cwd, './formatters/custom.js'), + extends: [], + rules: {} + }); +}); + +test('returns formatter name when unable to resolve from config directory', async t => { + const cwd = await git.bootstrap('fixtures/formatter-local-module'); + const actual = await load({formatter: './doesnt/exists.js'}, {cwd}); + + t.deepEqual(actual, { + formatter: './doesnt/exists.js', + extends: [], + rules: {} + }); +});