From 8d1103ef8ac68da5bc2ecc2be97caa81777a23a1 Mon Sep 17 00:00:00 2001 From: Marco Date: Fri, 5 Jan 2018 14:47:10 +0100 Subject: [PATCH] allow to execute subactions --- src/generator-runner.js | 38 +++++++++------ .../plop-templates/component.js | 2 + .../plop-templates/component.story.js | 4 ++ .../plop-templates/component.test.js | 2 + tests/sub-actions-mock/plopfile.js | 47 +++++++++++++++++++ tests/sub-actions.ava.js | 24 ++++++++++ 6 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 tests/sub-actions-mock/plop-templates/component.js create mode 100644 tests/sub-actions-mock/plop-templates/component.story.js create mode 100644 tests/sub-actions-mock/plop-templates/component.test.js create mode 100644 tests/sub-actions-mock/plopfile.js create mode 100644 tests/sub-actions.ava.js diff --git a/src/generator-runner.js b/src/generator-runner.js index e54c3da..6001192 100644 --- a/src/generator-runner.js +++ b/src/generator-runner.js @@ -13,33 +13,31 @@ export default function (plopfileApi) { // triggers inquirer with the correct prompts for this generator // returns a promise that resolves with the user's answers const runGeneratorPrompts = co.wrap(function* (genObject, bypassArr = []) { - const {prompts} = genObject; + const {prompts, name} = genObject; if (prompts == null) { - throw Error(`${genObject.name} has no prompts`); + throw Error(`${name} has no prompts`); } if (typeof prompts === 'function') { return yield prompts(plopfileApi.inquirer); } - + // handle bypass data when provided const [promptsAfterBypass, bypassAnswers] = promptBypass(prompts, bypassArr, plopfileApi); - + return yield plopfileApi.inquirer .prompt(promptsAfterBypass) .then(answers => Object.assign(answers, bypassAnswers)); }); // Run the actions for this generator - const runGeneratorActions = co.wrap(function* (genObject, data) { + const runGeneratorActions = co.wrap(function* ({actions, name}, data) { var changes = []; // array of changed made by the actions var failures = []; // array of actions that failed - var {actions} = genObject; // the list of actions to execute const customActionTypes = getCustomActionTypes(); const buildInActions = { add, addMany, modify }; const actionTypes = Object.assign({}, customActionTypes, buildInActions); - abort = false; // if action is a function, run it to get our array of actions @@ -47,12 +45,12 @@ export default function (plopfileApi) { // if actions are not defined... we cannot proceed. if (actions == null) { - throw Error(`${genObject.name} has no actions`); + throw Error(`${name} has no actions`); } // if actions are not an array, invalid! if (!(actions instanceof Array)) { - throw Error(`${genObject.name} has invalid actions`); + throw Error(`${name} has invalid actions`); } for (let [actionIdx, action] of actions.entries()) { @@ -81,8 +79,8 @@ export default function (plopfileApi) { } try { - const actionResult = yield executeActionLogic(actionLogic, actionCfg, data); - changes.push(actionResult); + const actionResults = yield executeActionLogic(actionLogic, actionCfg, data); + changes.push(...actionResults); } catch(failure) { failures.push(failure); } @@ -95,14 +93,26 @@ export default function (plopfileApi) { const executeActionLogic = co.wrap(function* (action, cfg, data) { const failure = makeErrorLogger(cfg.type || 'function', '', cfg.abortOnFail); + const actionApi = Object.assign({}, plopfileApi, { + runActions: co.wrap(function*(actions, data) { + const {changes, failures} = yield runGeneratorActions({actions, name: cfg.type}, data); + if(failures.length > 0) { + // FIXME: error should always be error Object + // the logger should do the proper formatting + throw failures.map(f => '\n ->\t'+f.error.toString()); + } + + return changes; + }) + }); // convert any returned data into a promise to // return and wait on - return yield Promise.resolve(action(data, cfg, plopfileApi)).then( + return yield Promise.resolve(action(data, cfg, actionApi)).then( // show the resolved value in the console - result => ({ + result => (Array.isArray(result) ? result : [{ type: cfg.type || 'function', path: colors.blue(result.toString()) - }), + }]), // a rejected promise is treated as a failure function (err) { throw failure(err.message || err.toString()); diff --git a/tests/sub-actions-mock/plop-templates/component.js b/tests/sub-actions-mock/plop-templates/component.js new file mode 100644 index 0000000..49646b0 --- /dev/null +++ b/tests/sub-actions-mock/plop-templates/component.js @@ -0,0 +1,2 @@ + +// this is a component named {{name}} diff --git a/tests/sub-actions-mock/plop-templates/component.story.js b/tests/sub-actions-mock/plop-templates/component.story.js new file mode 100644 index 0000000..bf73bb7 --- /dev/null +++ b/tests/sub-actions-mock/plop-templates/component.story.js @@ -0,0 +1,4 @@ + +import {{name}} from './{{name}}.js' +// this is a storybook file for a component named {{name}} +// (https://storybook.js.org/) diff --git a/tests/sub-actions-mock/plop-templates/component.test.js b/tests/sub-actions-mock/plop-templates/component.test.js new file mode 100644 index 0000000..8527115 --- /dev/null +++ b/tests/sub-actions-mock/plop-templates/component.test.js @@ -0,0 +1,2 @@ +import {{name}} from './{{name}}.js' +// this is a testfile for component named {{name}} diff --git a/tests/sub-actions-mock/plopfile.js b/tests/sub-actions-mock/plopfile.js new file mode 100644 index 0000000..8ee7d3f --- /dev/null +++ b/tests/sub-actions-mock/plopfile.js @@ -0,0 +1,47 @@ +module.exports = function(plop) { + 'use strict'; + + plop.setActionType( + 'component-with-tests-and-stories', + (answers, config, { runActions }) => { + return runActions( + [ + { + type: 'add', + path: './src/components/{{name}}.js', + templateFile: config.componentTemplate + }, + { + type: 'add', + path: './src/components/stories/{{name}}.story.js', + templateFile: config.storyTemplate + }, + { + type: 'add', + path: './src/components/tests/{{name}}.test.js', + templateFile: config.testTemplate + } + ], + answers + ); + } + ); + + plop.setGenerator('component', { + prompts: [ + { + type: 'input', + name: 'name', + message: 'What\'s the component name?' + } + ], + actions: [ + { + type: 'component-with-tests-and-stories', + componentTemplate: 'plop-templates/component.js', + testTemplate: 'plop-templates/component.test.js', + storyTemplate: 'plop-templates/component.story.js' + } + ] + }); +}; diff --git a/tests/sub-actions.ava.js b/tests/sub-actions.ava.js new file mode 100644 index 0000000..c76b887 --- /dev/null +++ b/tests/sub-actions.ava.js @@ -0,0 +1,24 @@ +import fs from 'fs'; +import path from 'path'; +import AvaTest from './_base-ava-test'; +const {test, mockPath, testSrcPath, nodePlop} = (new AvaTest(__filename)); + +const plop = nodePlop(`${mockPath}/plopfile.js`); +const componentGenerator = plop.getGenerator('component'); + +test.before(() => { + return componentGenerator.runActions({name: 'Header'}); +}); + +test('Check that all three files have been created', t => { + const componentFilePath = path.resolve(testSrcPath, 'components/Header.js'); + const componentTestFilePath = path.resolve(testSrcPath, 'components/tests/Header.test.js'); + const componentStoryFilePath = path.resolve(testSrcPath, 'components/stories/Header.story.js'); + + + // both files should have been created + t.true(fs.existsSync(componentFilePath), componentFilePath); + t.true(fs.existsSync(componentTestFilePath), componentTestFilePath); + t.true(fs.existsSync(componentStoryFilePath), componentStoryFilePath); + +});