Skip to content
This repository has been archived by the owner on Apr 26, 2022. It is now read-only.

implement generator mixins #61

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
226 changes: 178 additions & 48 deletions src/node-plop.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,42 @@ import bakedInHelpers from './baked-in-helpers';
import generatorRunner from './generator-runner';

function nodePlop(plopfilePath = '', plopCfg = {}) {

let pkgJson = {};
let defaultInclude = {generators: true};
let defaultInclude = { generators: true };

let welcomeMessage;
const {destBasePath, force} = plopCfg;
const { destBasePath, force } = plopCfg;
const generators = {};
const generatorMixins = {};
const partials = {};
const actionTypes = {};
const helpers = Object.assign({
pkg: (propertyPath) => _get(pkgJson, propertyPath, '')
}, bakedInHelpers);
const helpers = Object.assign(
{
pkg: propertyPath => _get(pkgJson, propertyPath, '')
},
bakedInHelpers
);
const baseHelpers = Object.keys(helpers);

const setPrompt = inquirer.registerPrompt;
const setWelcomeMessage = (message) => { welcomeMessage = message; };
const setHelper = (name, fn) => { helpers[name] = fn; };
const setPartial = (name, str) => { partials[name] = str; };
const setActionType = (name, fn) => { actionTypes[name] = fn; };
const setWelcomeMessage = message => {
welcomeMessage = message;
};
const setHelper = (name, fn) => {
helpers[name] = fn;
};
const setPartial = (name, str) => {
partials[name] = str;
};
const setActionType = (name, fn) => {
actionTypes[name] = fn;
};

function renderString(template, data) {
Object.keys(helpers).forEach(h => handlebars.registerHelper(h, helpers[h]));
Object.keys(partials).forEach(p => handlebars.registerPartial(p, partials[p]));
Object.keys(partials).forEach(p =>
handlebars.registerPartial(p, partials[p])
);
return handlebars.compile(template)(data);
}

Expand All @@ -53,17 +66,39 @@ function nodePlop(plopfilePath = '', plopCfg = {}) {
return generators[name];
}

const getHelperList = () => Object.keys(helpers).filter(h => !baseHelpers.includes(h));
const getGeneratorMixin = name => generatorMixins[name];
function setGeneratorMixin(name = '', config = {}) {
// if no name is provided, use a default
name = name || `generatorMixin-${Object.keys(generatorMixins).length + 1}`;

// add the generator to this context
generatorMixins[name] = Object.assign(config, {
name: name,
basePath: plopfilePath
});

return generatorMixins[name];
}

const getHelperList = () =>
Object.keys(helpers).filter(h => !baseHelpers.includes(h));
const getPartialList = () => Object.keys(partials);
const getActionTypeList = () => Object.keys(actionTypes);
function getGeneratorList() {
return Object.keys(generators).map(function (name) {
const {description} = generators[name];
return {name, description};
return Object.keys(generators).map(function(name) {
const { description } = generators[name];
return { name, description };
});
}

const setDefaultInclude = inc => defaultInclude = inc;
function getGeneratorMixinList() {
return Object.keys(generatorMixins).map(function(name) {
const { description } = generatorMixins[name];
return { name, description };
});
}

const setDefaultInclude = inc => (defaultInclude = inc);
const getDefaultInclude = () => defaultInclude;
const getDestBasePath = () => destBasePath || plopfilePath;
const getPlopfilePath = () => plopfilePath;
Expand All @@ -77,39 +112,75 @@ function nodePlop(plopfilePath = '', plopCfg = {}) {
};

function load(targets, loadCfg = {}, includeOverride) {
if (typeof targets === 'string') { targets = [targets]; }
const config = Object.assign({
destBasePath: getDestBasePath()
}, loadCfg);
if (typeof targets === 'string') {
targets = [targets];
}
const config = Object.assign(
{
destBasePath: getDestBasePath()
},
loadCfg
);

targets.forEach(function (target) {
const targetPath = resolve.sync(target, {basedir: getPlopfilePath()});
targets.forEach(function(target) {
const targetPath = resolve.sync(target, { basedir: getPlopfilePath() });
const proxy = nodePlop(targetPath, config);
const proxyDefaultInclude = proxy.getDefaultInclude() || {};
const includeCfg = includeOverride || proxyDefaultInclude;
const include = Object.assign({
generators: false,
helpers: false,
partials: false,
actionTypes: false
}, includeCfg);
const include = Object.assign(
{
generators: false,
generatorMixins: false,
helpers: false,
partials: false,
actionTypes: false
},
includeCfg
);

const genNameList = proxy.getGeneratorList().map(g => g.name);
loadAsset(genNameList, include.generators, setGenerator, proxyName => ({proxyName, proxy}));
loadAsset(proxy.getPartialList(), include.partials, setPartial, proxy.getPartial);
loadAsset(proxy.getHelperList(), include.helpers, setHelper, proxy.getHelper);
loadAsset(proxy.getActionTypeList(), include.actionTypes, setActionType, proxy.getActionType);
const genMixinNameList = proxy.getGeneratorList().map(g => g.name);
loadAsset(genNameList, include.generators, setGenerator, proxyName => ({
proxyName,
proxy
}));
loadAsset(
genMixinNameList,
include.generatorMixins,
setGeneratorMixin,
proxyName => ({ proxyName, proxy })
);
loadAsset(
proxy.getPartialList(),
include.partials,
setPartial,
proxy.getPartial
);
loadAsset(
proxy.getHelperList(),
include.helpers,
setHelper,
proxy.getHelper
);
loadAsset(
proxy.getActionTypeList(),
include.actionTypes,
setActionType,
proxy.getActionType
);
});
}

function loadAsset(nameList, include, addFunc, getFunc) {
var incArr;
if (include === true) { incArr = nameList; }
if (include === true) {
incArr = nameList;
}
if (include instanceof Array) {
incArr = include.filter(n => typeof n === 'string');
}
if (incArr != null) {
include = incArr.reduce(function (inc, name) {
include = incArr.reduce(function(inc, name) {
inc[name] = name;
return inc;
}, {});
Expand All @@ -122,8 +193,11 @@ function nodePlop(plopfilePath = '', plopCfg = {}) {

function loadPackageJson() {
// look for a package.json file to use for the "pkg" helper
try { pkgJson = require(path.join(getDestBasePath(), 'package.json')); }
catch(error) { pkgJson = {}; }
try {
pkgJson = require(path.join(getDestBasePath(), 'package.json'));
} catch (error) {
pkgJson = {};
}
}

/////////
Expand All @@ -134,24 +208,40 @@ function nodePlop(plopfilePath = '', plopCfg = {}) {
const plopfileApi = {
// main methods for setting and getting plop context things
setPrompt,
setWelcomeMessage, getWelcomeMessage,
setGenerator, getGenerator, getGeneratorList,
setPartial, getPartial, getPartialList,
setHelper, getHelper, getHelperList,
setActionType, getActionType, getActionTypeList,
setWelcomeMessage,
getWelcomeMessage,
setGenerator,
getGenerator,
getGeneratorList,
setGeneratorMixin,
getGeneratorMixin,
getGeneratorMixinList,
setPartial,
getPartial,
getPartialList,
setHelper,
getHelper,
getHelperList,
setActionType,
getActionType,
getActionTypeList,

// path context methods
setPlopfilePath, getPlopfilePath,
setPlopfilePath,
getPlopfilePath,
getDestBasePath,

// plop.load functionality
load, setDefaultInclude, getDefaultInclude,
load,
setDefaultInclude,
getDefaultInclude,

// render a handlebars template
renderString,

// passthrough properties
inquirer, handlebars,
inquirer,
handlebars,

// passthroughs for backward compatibility
addPrompt: setPrompt,
Expand All @@ -160,10 +250,49 @@ function nodePlop(plopfilePath = '', plopCfg = {}) {
};

// the runner for this instance of the nodePlop api
const runner = generatorRunner(plopfileApi, {force});

const runner = generatorRunner(plopfileApi, { force });

/**
if actions is a funciton, it gets invoked with ...args and returned
otherwise actions is assumed to be an array and returned directly,
so in every case it will be an array
**/
const normalizeActions = (actions, ...args) => {
if (actions && typeof actions === 'function') {
return actions(...args) || [];
}
return actions || [];
};
// thx https://medium.com/@dtipson/creating-an-es6ish-compose-in-javascript-ac580b95104a
const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
const applyGeneratorMixins = generator => {
const mixins = generator.mixins
.map(name => getGeneratorMixin(name))
.reverse();
const mixinActions = compose(
...mixins.filter(m => m.actions).map(m => m.actions)
);
const mixinPrompts = compose(
...mixins.filter(m => m.prompts).map(m => m.prompts)
);

return Object.assign({}, generator, {
actions: (...actionsArgs) =>
mixinActions(
normalizeActions(generator.actions, ...actionsArgs),
...actionsArgs
),
prompts: mixinPrompts(generator.prompts)
});
};

const nodePlopApi = Object.assign({}, plopfileApi, {
getGenerator(name) {
var generator = plopfileApi.getGenerator(name);
let generator = plopfileApi.getGenerator(name);
if (generator.mixins) {
generator = applyGeneratorMixins(generator);
}

// if this generator was loaded from an external plopfile, proxy the
// generator request through to the external plop instance
Expand All @@ -172,8 +301,9 @@ function nodePlop(plopfilePath = '', plopCfg = {}) {
}

return Object.assign({}, generator, {
runActions: (data) => runner.runGeneratorActions(generator, data),
runPrompts: (bypassArr = []) => runner.runGeneratorPrompts(generator, bypassArr)
runActions: data => runner.runGeneratorActions(generator, data),
runPrompts: (bypassArr = []) =>
runner.runGeneratorPrompts(generator, bypassArr)
});
},
setGenerator(name, config) {
Expand Down
1 change: 1 addition & 0 deletions tests/generator-mixins-mock/plop-templates/moduleFile.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is a file named {{name}} inside module {{moduleName}}
1 change: 1 addition & 0 deletions tests/generator-mixins-mock/plop-templates/moduleIndex.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is the module index file for module {{moduleName}}
Loading