From 142607e9dd95433e2fedeebe195b7c69712d70a8 Mon Sep 17 00:00:00 2001 From: David Sveningsson Date: Thu, 8 Jul 2021 21:57:48 +0200 Subject: [PATCH 1/2] feat: allow to specify `preset` in user configuration --- docs/Making_presets.md | 10 ++++++++++ packages/mrm/bin/mrm.js | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/Making_presets.md b/docs/Making_presets.md index b494483a..ebd0e69b 100644 --- a/docs/Making_presets.md +++ b/docs/Making_presets.md @@ -59,3 +59,13 @@ The package name should should follow this pattern: `mrm-preset-`, otherwi mrm license --preset unicorn # mrm-preset-unicorn mrm license --preset @mycompany/unicorn-preset # @mycompany/unicorn-preset ``` + +## Default preset + +The default preset can be configured in `.mrm/config.json`: + +```json +{ + "preset": "@mycompany/unicorn" +} +``` diff --git a/packages/mrm/bin/mrm.js b/packages/mrm/bin/mrm.js index a3429942..c24c9dd7 100755 --- a/packages/mrm/bin/mrm.js +++ b/packages/mrm/bin/mrm.js @@ -14,6 +14,7 @@ const { random } = require('middleearth-names'); const { run, getConfig, + getConfigFromFile, getAllTasks, resolveUsingNpx, getPackageName, @@ -64,8 +65,12 @@ async function main() { const binaryName = binaryPath && binaryPath.endsWith('/npx') ? 'npx mrm' : 'mrm'; + // Load user configuration from default directories first (e.g. `~/.mrm/config.json`). + // This does not include configuration from command line arguments, presets or the local project. + const userConfig = await getConfigFromFile(defaultDirectories, 'config.json'); + // Preset - const preset = argv.preset || 'default'; + const preset = argv.preset || userConfig.preset || 'default'; const isDefaultPreset = preset === 'default'; const directories = await resolveDirectories(defaultDirectories); const options = await getConfig(directories, 'config.json', argv); From c5d6f045a156fc1dd135621aea1219ef575a1c23 Mon Sep 17 00:00:00 2001 From: David Sveningsson Date: Thu, 8 Jul 2021 22:27:58 +0200 Subject: [PATCH 2/2] feat: show preset name when running task --- packages/mrm/bin/mrm.js | 2 +- packages/mrm/src/__tests__/index.spec.js | 113 +++++++++++++++++++---- packages/mrm/src/index.js | 25 +++-- 3 files changed, 112 insertions(+), 28 deletions(-) diff --git a/packages/mrm/bin/mrm.js b/packages/mrm/bin/mrm.js index c24c9dd7..b356f3f8 100755 --- a/packages/mrm/bin/mrm.js +++ b/packages/mrm/bin/mrm.js @@ -77,7 +77,7 @@ async function main() { if (tasks.length === 0 || tasks[0] === 'help') { commandHelp(); } else { - run(tasks, directories, options, argv).catch(err => { + run(tasks, directories, preset, options, argv).catch(err => { if (err.constructor === MrmUnknownAlias) { printError(err.message); } else if (err.constructor === MrmUnknownTask) { diff --git a/packages/mrm/src/__tests__/index.spec.js b/packages/mrm/src/__tests__/index.spec.js index a3f1eaf7..c3a0734e 100644 --- a/packages/mrm/src/__tests__/index.spec.js +++ b/packages/mrm/src/__tests__/index.spec.js @@ -2,6 +2,7 @@ /* eslint-disable no-console */ const path = require('path'); +const kleur = require('kleur'); const { promiseFirst, tryFile, @@ -28,6 +29,7 @@ const task6 = require('../../test/dir3/task6'); const task8 = require('../../test/dir5/task8'); const configFile = 'config.json'; +const defaultPreset = 'default'; const directories = [ path.resolve(__dirname, '../../test/dir1'), path.resolve(__dirname, '../../test/dir2'), @@ -53,6 +55,9 @@ const argv = { const file = name => path.join(__dirname, '../../test', name); +// Disable color output for output generated by test-cases +kleur.enabled = false; + describe('promiseFirst', () => { it('should return the first resolving function', async () => { const result = await promiseFirst([ @@ -311,7 +316,7 @@ describe('runTask', () => { it('should run a module', () => { return new Promise((resolve, reject) => { - runTask('task1', directories, {}, {}) + runTask('task1', directories, defaultPreset, {}, {}) .then(() => { expect(task1).toHaveBeenCalledTimes(1); resolve(); @@ -322,7 +327,9 @@ describe('runTask', () => { it('should pass a config function and a params object to a module', () => { return new Promise((resolve, reject) => { - runTask('task1', directories, options, { interactive: false }) + runTask('task1', directories, defaultPreset, options, { + interactive: false, + }) .then(() => { expect(task1).toHaveBeenCalledWith( expect.objectContaining({ pizza: 'salami' }), @@ -335,7 +342,13 @@ describe('runTask', () => { }); it('should throw when module not found', () => { - const pizza = runTask('this-does-not-exist-on-npm', directories, {}, {}); + const pizza = runTask( + 'this-does-not-exist-on-npm', + directories, + defaultPreset, + {}, + {} + ); // ideally we can use toThrowError but that works with >= jest@22 // https://github.com/facebook/jest/issues/5076 @@ -346,7 +359,7 @@ describe('runTask', () => { }, 20000); it('should throw when task module is invalid', () => { - const result = runTask('task7', directories, {}, {}); + const result = runTask('task7', directories, defaultPreset, {}, {}); // ideally we can use toThrowError but that works with >= jest@22 // https://github.com/facebook/jest/issues/5076 @@ -358,7 +371,7 @@ describe('runTask', () => { it('should run an async module', () => { return new Promise((resolve, reject) => { - runTask('task4', directories, {}, { stack: [] }) + runTask('task4', directories, defaultPreset, {}, { stack: [] }) .then(() => { expect(task4).toHaveBeenCalledTimes(1); expect(task4.mock.calls[0][1].stack).toEqual(['Task 2.4']); @@ -374,7 +387,13 @@ describe('runTask', () => { 'second-config': 'other value', }); - await runTask('task6', directories, {}, { interactive: true }); + await runTask( + 'task6', + directories, + defaultPreset, + {}, + { interactive: true } + ); expect(task6).toHaveBeenCalledTimes(1); expect(task6).toHaveBeenCalledWith( @@ -389,7 +408,13 @@ describe('runTask', () => { it('should respect config defaults from task parameters when interactive mode is on', async () => { configureInquirer({ 'first-config': 'value' }); - await runTask('task6', directories, {}, { interactive: true }); + await runTask( + 'task6', + directories, + defaultPreset, + {}, + { interactive: true } + ); expect(task6).toHaveBeenCalledTimes(1); expect(task6).toHaveBeenCalledWith( @@ -402,7 +427,13 @@ describe('runTask', () => { }); it('should respect config defaults from task parameters when interactive mode is off', async () => { - await runTask('task6', directories, {}, { interactive: false }); + await runTask( + 'task6', + directories, + defaultPreset, + {}, + { interactive: false } + ); expect(task6).toHaveBeenCalledTimes(1); expect(task6).toHaveBeenCalledWith( @@ -415,9 +446,33 @@ describe('runTask', () => { }); it('should run normally when interactive mode is on but task has no interactive parameters', async () => { - await runTask('task3', directories, {}, { interactive: true }); + await runTask( + 'task3', + directories, + defaultPreset, + {}, + { interactive: true } + ); expect(task3).toHaveBeenCalledTimes(1); }); + + it('should log when using default preset', async () => { + expect.assertions(1); + const spy = jest.spyOn(console, 'log').mockReturnValue(undefined); + await runTask('task1', directories, defaultPreset, {}, {}); + expect(spy).toHaveBeenCalledWith('Running task1...'); + spy.mockRestore(); + }); + + it('should log when using custom preset', async () => { + expect.assertions(1); + const spy = jest.spyOn(console, 'log').mockReturnValue(undefined); + await runTask('task1', directories, 'custom-preset', {}, {}); + expect(spy).toHaveBeenCalledWith( + 'Running task1 (from preset "custom-preset")...' + ); + spy.mockRestore(); + }); }); describe('runAlias', () => { @@ -431,7 +486,9 @@ describe('runAlias', () => { it('should run all tasks defined in an alias', () => { return new Promise((resolve, reject) => { - runAlias('alias1', directories, optionsWithAliases, { stack: [] }) + runAlias('alias1', directories, defaultPreset, optionsWithAliases, { + stack: [], + }) .then(() => { expect(task1).toHaveBeenCalledTimes(1); expect(task2).toHaveBeenCalledTimes(0); @@ -445,7 +502,13 @@ describe('runAlias', () => { }); it('should throw when alias not found', () => { - const pizza = runAlias('pizza', directories, optionsWithAliases, {}); + const pizza = runAlias( + 'pizza', + directories, + defaultPreset, + optionsWithAliases, + {} + ); return expect(pizza).rejects.toHaveProperty( 'message', 'Alias “pizza” not found.' @@ -456,7 +519,9 @@ describe('runAlias', () => { return new Promise((resolve, reject) => { const stack = []; - runAlias(['alias2'], directories, optionsWithAliases, { stack }) + runAlias(['alias2'], directories, defaultPreset, optionsWithAliases, { + stack, + }) .then(() => { expect(task4).toHaveBeenCalledTimes(1); expect(task5).toHaveBeenCalledTimes(1); @@ -479,7 +544,7 @@ describe('run', () => { it('should run a task', () => { return new Promise((resolve, reject) => { - run('task1', directories, optionsWithAliases, {}) + run('task1', directories, defaultPreset, optionsWithAliases, {}) .then(() => { expect(task1).toHaveBeenCalledTimes(1); resolve(); @@ -490,7 +555,9 @@ describe('run', () => { it('should run all tasks defined in an alias', () => { return new Promise((resolve, reject) => { - run('alias1', directories, optionsWithAliases, { stack: [] }) + run('alias1', directories, defaultPreset, optionsWithAliases, { + stack: [], + }) .then(() => { expect(task1).toHaveBeenCalledTimes(1); expect(task2).toHaveBeenCalledTimes(0); @@ -505,9 +572,15 @@ describe('run', () => { it('should run multiple tasks', () => { return new Promise((resolve, reject) => { - run(['task1', 'task2', 'task4'], directories, optionsWithAliases, { - stack: [], - }) + run( + ['task1', 'task2', 'task4'], + directories, + defaultPreset, + optionsWithAliases, + { + stack: [], + } + ) .then(() => { expect(task1).toHaveBeenCalledTimes(1); expect(task2).toHaveBeenCalledTimes(1); @@ -522,7 +595,9 @@ describe('run', () => { it('should run multiple tasks in sequence', () => { return new Promise((resolve, reject) => { const stack = []; - run(['task4', 'task5'], directories, optionsWithAliases, { stack }) + run(['task4', 'task5'], directories, defaultPreset, optionsWithAliases, { + stack, + }) .then(() => { expect(task4).toHaveBeenCalledTimes(1); expect(task5).toHaveBeenCalledTimes(1); @@ -535,7 +610,7 @@ describe('run', () => { it('should run a task in a custom preset', () => { return new Promise((resolve, reject) => { - run('task1', presetDir, optionsWithAliases, {}) + run('task1', presetDir, defaultPreset, optionsWithAliases, {}) .then(() => { expect(task1).toHaveBeenCalledTimes(1); resolve(); diff --git a/packages/mrm/src/index.js b/packages/mrm/src/index.js index 9a696a42..9b3fec29 100644 --- a/packages/mrm/src/index.js +++ b/packages/mrm/src/index.js @@ -66,15 +66,16 @@ function promiseSeries(items, iterator) { * * @param {string|string[]} name * @param {string[]} directories + * @param {string} preset * @param {Object} options * @param {Object} argv * @returns {Promise} */ -function run(name, directories, options, argv) { +function run(name, directories, preset, options, argv) { if (Array.isArray(name)) { return new Promise((resolve, reject) => { promiseSeries(name, n => { - return run(n, directories, options, argv); + return run(n, directories, preset, options, argv); }) .then(() => resolve()) .catch(reject); @@ -82,9 +83,9 @@ function run(name, directories, options, argv) { } if (getAllAliases(options)[name]) { - return runAlias(name, directories, options, argv); + return runAlias(name, directories, preset, options, argv); } - return runTask(name, directories, options, argv); + return runTask(name, directories, preset, options, argv); } /** @@ -92,11 +93,12 @@ function run(name, directories, options, argv) { * * @param {string} aliasName * @param {string[]} directories + * @param {string} preset * @param {Object} options * @param {Object} [argv] * @returns {Promise} */ -function runAlias(aliasName, directories, options, argv) { +function runAlias(aliasName, directories, preset, options, argv) { return new Promise((resolve, reject) => { const tasks = getAllAliases(options)[aliasName]; if (!tasks) { @@ -107,7 +109,7 @@ function runAlias(aliasName, directories, options, argv) { console.log(kleur.yellow(`Running alias ${aliasName}...`)); promiseSeries(tasks, name => { - return runTask(name, directories, options, argv); + return runTask(name, directories, preset, options, argv); }) .then(() => resolve()) .catch(reject); @@ -133,11 +135,12 @@ function getPackageName(type, packageName) { * * @param {string} taskName * @param {string[]} directories + * @param {string} preset * @param {Object} options * @param {Object} [argv] * @returns {Promise} */ -async function runTask(taskName, directories, options, argv) { +async function runTask(taskName, directories, preset, options, argv) { const taskPackageName = getPackageName('task', taskName); let modulePath; try { @@ -169,7 +172,13 @@ async function runTask(taskName, directories, options, argv) { return; } - console.log(kleur.cyan(`Running ${taskName}...`)); + if (preset === 'default') { + console.log(kleur.cyan(`Running ${taskName}...`)); + } else { + console.log( + kleur.cyan(`Running ${taskName} (from preset "${preset}")...`) + ); + } Promise.resolve(getTaskOptions(module, argv.interactive, options)) .then(config => module(config, argv))