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

Commit

Permalink
Merge pull request #1 from crystian/refactor-possibilities
Browse files Browse the repository at this point in the history
Refactor possibilities
  • Loading branch information
crystian authored May 10, 2018
2 parents 0c412ff + c216d09 commit 2db2026
Show file tree
Hide file tree
Showing 13 changed files with 432 additions and 537 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ Este comando (`e`) es un CLI que se ejecuta en el contexto donde es ejecutado, o

A.k.a: `executor.json`

![](docs/img/tool2.jpg)

Toda la magia se configura desde este archivo. Como mencione antes **no es necesario un proyecto "node"**, esta tool sirve para cualquier tipo de proyecto en el que quieras tener atajos de comandos.

## Json structure
Expand Down
Binary file added docs/img/tool2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
118 changes: 26 additions & 92 deletions lib/builders.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,91 +14,6 @@ const buildConfig = (configDefault, config) => {
};


/**
* Interpolate all the inputs!
*
* Inputs in order: environment, predefined, template, shortcut
* And it allow to use nested object! (not for environment)
*
* Note: it can be changed to get better performance.
*
* @param config
* @returns {any}
*/
const buildInterpolateVariables = (config) => {
let shortcuts = config.shortcuts,
templates = config.templates || {},
environments = config.environmentsRendered || [],
newShortcuts, newTemplates, newEnvironments;

if (!shortcuts) {
throw new Error(messages.shortcuts.notFound);
}

// copy structure to mutate in the easy way
newShortcuts = JSON.parse(JSON.stringify(shortcuts));
newTemplates = JSON.parse(JSON.stringify(templates));

newEnvironments = _buildInterpolateEnvironmentVars(environments);

// This is recursive and it is needed to pass on each stage
// to template
_buildInterpolateVariablesRecursive(newTemplates, newEnvironments);
_buildInterpolateVariablesRecursive(newTemplates, { predefined });
_buildInterpolateVariablesRecursive(newTemplates, newTemplates); // yes to itself!
// to shortcuts
_buildInterpolateVariablesRecursive(newShortcuts, newEnvironments);
_buildInterpolateVariablesRecursive(newShortcuts, { predefined });
_buildInterpolateVariablesRecursive(newShortcuts, newTemplates);
_buildInterpolateVariablesRecursive(newShortcuts, newShortcuts, true); // yes to itself!

return newShortcuts;
};

/**
* It gets data from the external environment, and prepare it to use.
*
* @param environments
* @private
*/
const _buildInterpolateEnvironmentVars = (environments) => {
let environmentsToReturn = {};

environments.forEach(item => {
let key = Object.keys(item)[0];
environmentsToReturn[key] = process.env[item[key]];
});

return environmentsToReturn;
};

/**
* It does the magic recursively to replace the placeholders with values, even itself
*
* @param values are the data and you can use it so simple or you can mix with templates (by placeholders)
* @param templates are the reuse "snippet", you can use it on values and avoid to repeat your code!
* @param finalFlag at the final it will check if there are some placeholders without replaced
* @private
*/
const _buildInterpolateVariablesRecursive = (values, templates, finalFlag) => {

Object.keys(values).forEach(item => {

// be careful, it is a mutable object
if (isString(values[item])) {
values[item] = values[item].toTemplate(templates);

if (finalFlag && values[item].match(/\${.+?}/g)) {
throw new Error(messages.templates.notFound.toTemplate({ template: values[item] }));
}
} else if (isObject(values[item]) && !isObjectEmpty(values[item])) {
_buildInterpolateVariablesRecursive(values[item], templates, finalFlag);
} else {
throw new Error(messages.templates.invalidFormat.toTemplate({ key: item, value: values[item] }));
}
});
};

/**
* This build the command line joining all pieces recursively
*
Expand All @@ -108,14 +23,36 @@ const _buildInterpolateVariablesRecursive = (values, templates, finalFlag) => {
*/
const buildShortcutCommand = (shortcuts, shortcut) => {
shortcut = shortcut.split(' ');
let log = messages.shortcut.notFoundFirstShortcut,
let log = messages.shortcut.notFoundFirstShortcut.toTemplate({shortcut}) + buildShortcutPossibilities(shortcuts),
command = _buildShortcutRecursive(shortcuts, shortcut, log);

// this add final arguments to the command line
command = command.split(' ').concat(shortcut);
return command;
};

/**
* This build the possibilities to use for output to user
*
* @param shortcuts
* @returns {string}
*/
const buildShortcutPossibilities = (shortcuts) => {
let r = '';

if (shortcuts) {
let shortcutsMap = Object.keys(shortcuts).map(item => {
return `"${item}"`;
});

if (shortcutsMap.length > 0) {
r = messages.shortcut.withoutArgumentsPossibleValues.toTemplate({ shortcuts: shortcutsMap.join(', ') });
}
}

return r;
};

/**
* The recursive method to chain the shortcut with its templates
*
Expand All @@ -135,11 +72,8 @@ const _buildShortcutRecursive = (shortcuts, shortcut, log) => {

let log = messages.shortcut.notFound.toTemplate({ key });

log += Object.keys(found).map(v => {
return ` "${v}"`;
}).join(',');

return _buildShortcutRecursive(found, shortcut, log);
return _buildShortcutRecursive(found, shortcut, log + buildShortcutPossibilities(found));
}

return found;
Expand All @@ -148,6 +82,6 @@ const _buildShortcutRecursive = (shortcuts, shortcut, log) => {

module.exports = {
buildConfig,
buildInterpolateVariables,
buildShortcutCommand
buildShortcutCommand,
buildShortcutPossibilities
};
15 changes: 4 additions & 11 deletions lib/command.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const { validateAndBuildEnvironments, validateShortcut, validateShortcuts, validateTemplates, validateConfig } = require('./validators');
const { isTestRunning, setColors } = require('./utils');
const { buildInterpolateVariables, buildShortcutCommand, buildConfig } = require('./builders');
const { buildShortcutCommand, buildConfig, buildShortcutPossibilities } = require('./builders');
const { getConfigFromCWD } = require('./node');
const { interpolateVariables } = require('./interpolate');
const { configDefault } = require('./defaults');
const { messages } = require('./i18n');

Expand Down Expand Up @@ -32,22 +33,14 @@ let buildCommandWithConfig = (shortcut, config) => {
setColors(config.config);

// validation tasks
let shortcutValid = validateShortcut(shortcut);
validateShortcut(shortcut, buildShortcutPossibilities(config.shortcuts));
validateConfig(config.config);
config.environmentsRendered = validateAndBuildEnvironments(config.environments);
validateTemplates(config.templates);
validateShortcuts(config.shortcuts);

if(!shortcutValid) {
let shortcuts = Object.keys(config.shortcuts).map(item => {
return `"${item}"`;
}).join(', ');

throw new Error(messages.shortcut.withoutArguments.toTemplate({shortcuts}));
}

// building tasks
config.shortcutsRendered = buildInterpolateVariables(config);
config.shortcutsRendered = interpolateVariables(config);

let command = buildShortcutCommand(config.shortcutsRendered, shortcut).join(' ');

Expand Down
27 changes: 14 additions & 13 deletions lib/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,29 @@ const messages = {
}
},
environments: {
invalidFormat: '[environments] The attribute "environments" should be an array with strings or simple object {key: value}'
invalidFormat: 'The attribute "environments" should be an array with strings or simple object {key: value}'
},
config: {
notFound: '"${fileName}" not found on this folder. Have you read the docs? Don\'t be lazy https://github.com/crystian/executor',
used: '[config] Config file used: "${configFileName}"',
invalidFormat: '[config] An invalid structure on config attribute',
invalidJson: '[config] Invalid json format on your configuration file: ${jsonFile}',
isNotAString: '[config] An invalid type on configuration file: "configFile" should be a string'
used: 'Config file used: "${configFileName}"',
invalidFormat: 'An invalid structure on config attribute',
invalidJson: 'Invalid json format on your configuration file: ${jsonFile}',
isNotAString: 'An invalid type on configuration file: "configFile" should be a string'
},
templates: {
notFound: '[template] Key "${template}" not found (remember the order is important!)',
invalidData: '[template] The configurations of templates are invalid, should be an object or string',
invalidFormat: '[template] Invalid format, remember should be a pair key value (both should be strings): "${key}": "${value}"'
notFound: 'Key "${template}" not found (remember the order is important!)',
invalidData: 'The configurations of templates are invalid, should be an object or string',
invalidFormat: 'Invalid format, remember should be a pair key value (both should be strings): "${key}": "${value}"'
},
shortcuts: {
notFound: '[shortcuts] The attribute "shortcuts" should exist and should be an object no empty.',
shouldBeAnObject: '[shortcuts] The attribute "shortcuts" should be an object, and the last attribute on it should be a string.'
notFound: 'The attribute "shortcuts" should exist and should be an object no empty.',
shouldBeAnObject: 'The attribute "shortcuts" should be an object, and the last attribute on it should be a string.'
},
shortcut: {
withoutArguments: '[argument] coff coff ... and the "shortcut"?\nPossible shortcuts configured: ${shortcuts}',
notFoundFirstShortcut: '[argument] The "shortcut" does not found, check your spelling or your configuration file',
notFound: '[argument] The sub-arguments from "${key}" does not match with your configuration file, for your key you have the following arguments: '
withoutArguments: 'coff coff ... and the "shortcut"?',
withoutArgumentsPossibleValues: '\n\nShortcuts configured: ${shortcuts}',
notFoundFirstShortcut: 'The shortcut: "${shortcut}" not found',
notFound: 'The sub-arguments from "${key}" does not match with your configuration file.'
}
};

Expand Down
94 changes: 94 additions & 0 deletions lib/interpolate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const { isObject, isObjectEmpty, isString } = require('./utils');
const { predefined } = require('./defaults');
const { messages } = require('./i18n');


/**
* Interpolate all the inputs!
*
* Inputs in order: environment, predefined, template, shortcut
* And it allow to use nested object! (not for environment)
*
* Note: it can be changed to get better performance.
*
* @param config
* @returns {any}
*/
const interpolateVariables = (config) => {
let shortcuts = config.shortcuts,
templates = config.templates || {},
environments = config.environmentsRendered || [],
newShortcuts, newTemplates, newEnvironments;

if (!shortcuts) {
throw new Error(messages.shortcuts.notFound);
}

// copy structure to mutate in the easy way
newShortcuts = JSON.parse(JSON.stringify(shortcuts));
newTemplates = JSON.parse(JSON.stringify(templates));

newEnvironments = _buildInterpolateEnvironmentVars(environments);

// This is recursive and it is needed to pass on each stage
// to template
_buildInterpolateVariablesRecursive(newTemplates, newEnvironments);
_buildInterpolateVariablesRecursive(newTemplates, { predefined });
_buildInterpolateVariablesRecursive(newTemplates, newTemplates); // yes to itself!
// to shortcuts
_buildInterpolateVariablesRecursive(newShortcuts, newEnvironments);
_buildInterpolateVariablesRecursive(newShortcuts, { predefined });
_buildInterpolateVariablesRecursive(newShortcuts, newTemplates);
_buildInterpolateVariablesRecursive(newShortcuts, newShortcuts, true); // yes to itself!

return newShortcuts;
};

/**
* It gets data from the external environment, and prepare it to use.
*
* @param environments
* @private
*/
const _buildInterpolateEnvironmentVars = (environments) => {
let environmentsToReturn = {};

environments.forEach(item => {
let key = Object.keys(item)[0];
environmentsToReturn[key] = process.env[item[key]];
});

return environmentsToReturn;
};

/**
* It does the magic recursively to replace the placeholders with values, even itself
*
* @param values are the data and you can use it so simple or you can mix with templates (by placeholders)
* @param templates are the reuse "snippet", you can use it on values and avoid to repeat your code!
* @param finalFlag at the final it will check if there are some placeholders without replaced
* @private
*/
const _buildInterpolateVariablesRecursive = (values, templates, finalFlag) => {

Object.keys(values).forEach(item => {

// be careful, it is a mutable object
if (isString(values[item])) {
values[item] = values[item].toTemplate(templates);

if (finalFlag && values[item].match(/\${.+?}/g)) {
throw new Error(messages.templates.notFound.toTemplate({ template: values[item] }));
}
} else if (isObject(values[item]) && !isObjectEmpty(values[item])) {
_buildInterpolateVariablesRecursive(values[item], templates, finalFlag);
} else {
throw new Error(messages.templates.invalidFormat.toTemplate({ key: item, value: values[item] }));
}
});
};


module.exports = {
interpolateVariables
};
2 changes: 1 addition & 1 deletion lib/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ let getConfigFileName = (filePath) => {
/* istanbul ignore if */
if (!isTestRunning()) {
// it is ok, I can use "console.primary" because I don't have the configuration to finish with this
console.log('\x1b[37m%s\x1b[0m', `\n[${messages.app.name}]`, messages.config.used.toTemplate({configFileName}));
console.log('\x1b[37m%s\x1b[0m', `[${messages.app.name}]`, messages.config.used.toTemplate({configFileName}));
}
}

Expand Down
7 changes: 5 additions & 2 deletions lib/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ const { messages } = require('./i18n');
* validate the input from the cli
*
* @param shortcut is the input from the user
* @param possibilities a string with possibilities to concatenate
*/
const validateShortcut = (shortcut) => {
return !!(shortcut && shortcut !== '' && isString(shortcut));
const validateShortcut = (shortcut, possibilities) => {
if (!(shortcut && shortcut !== '' && isString(shortcut))) {
throw new Error(messages.shortcut.withoutArguments + possibilities);
}
};


Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "executor",
"version": "0.0.8-beta",
"version": "0.0.9-beta",
"license": "MIT",
"description": "A powerful short-cutter to your console for you and your team",
"scripts": {
Expand All @@ -14,7 +14,7 @@
"devDependencies": {
"chai": "4.1.2",
"mocha": "5.1.1",
"nyc": "11.7.1"
"nyc": "11.7.3"
},
"nyc": {
"include": [
Expand Down
Loading

0 comments on commit 2db2026

Please sign in to comment.