Issues (23)

src/input.js (4 issues)

1
import process from 'process';
2
import fs from 'fs';
3
import { promisify, props, resolve } from 'bluebird';
4
import {
5
    T,
6
    __,
7
    assoc,
8
    concat,
9
    cond,
10
    contains,
11
    curry,
12
    filter as filterWhere,
13
    has,
14
    keys,
15
    map,
16
    mapObjIndexed,
17
    merge,
18
    pathEq,
19
    reduce,
20
    toPairs,
21
    when
22
} from 'ramda';
23
import { cyan, green, red, yellow } from 'colors/safe';
24
import { createPromptModule } from 'inquirer';
25
import { validator, filter } from './types';
26
import getAutocompleteSources, { compileClosure } from './autocomplete';
27
28
/**
29
 * Emits an info message to stdout
30
 *
31
 * @param {String} message
32
 * @return {Promise}
33
 */
34
export const emitInfo = concat(' ℹ Info: ') & cyan & console.log & resolve;
35
36
/**
37
 * Emits a warning to stdout
38
 *
39
 * @param {String} message
40
 * @return {Promise}
41
 */
42
export const emitWarning = concat(' ⚠ Warning: ') & yellow & console.log & resolve;
43
44
/**
45
 * Emits an error to stdout
46
 *
47
 * @param {String} message
48
 * @return {Promise}
49
 */
50
export const emitError = concat(' ✗ Error: ') & red & console.log & resolve;
51
52
/**
53
 * Emits a success message
54
 *
55
 * @param {String} message
56
 * @return {Promise}
57
 */
58
export const emitSuccess = concat(' ✔ Success: ') & green & console.log & resolve;
59
60
/**
61
 * Renames the keys of an object
62
 *
63
 * @sig {a: b} -> {a: *} -> {b: *}
64
 */
65
const renameKeys = curry((keysMap, obj) => reduce((acc, key) =>
66
    assoc(keysMap[key] || key, obj[key], acc), {}, keys(obj)));
67
68
const objectToChoices = toPairs & map(([value, name]) => ({ name, value }));
69
70
export const components = {
71
    Calendar: ~{
72
        type: 'datetime',
73
        format: ['m', '/', 'd', '/', 'yy'],
74
        filter: filter.Calendar },
75
    Char: ({ length }) => ({ filter: filter.Char(length) }),
76
    Checkbox: ~{ type: 'confirm' },
77
    Color: ~{
78
        type: 'chalk-pipe',
79
        validate: validator.Color },
80
    DoubleRange: ({ from, to }) => ({
81
        filter: filter.Double,
82
        validate: validator.Range(from, to) }),
83
    DateTime: ~{ type: 'datetime' },
84
    Double: ~{ validate: validator.Double, filter: filter.Double },
85
    Email: ~{ validate: validator.Email },
86
    Integer: ~{ validate: validator.Integer, filter: filter.Integer },
87
    IntegerRange: ({ from, to }) => ({
88
        filter: filter.Integer,
89
        validate: validator.Range(from, to) }),
90
    IntegerMultiRange: ({ from, to }) => ({
91
        filter: filter.IntegerMultiRange,
92
        validate: validator.IntegerMultiRange(from, to) }),
93
    Natural: ~{ validate: validator.Natural, filter: filter.Integer },
94
    OneOf: ({ values }) => ({
95
        type: 'list',
96
        choices: values,
97
        validate: validator.OneOf(values) }),
98
    String: ~{ type: 'input' },
99
    Url: ~{ validate: validator.Url },
100
    Money: ~{ validate: validator.Money, filter: filter.Money },
101
    SelectBox: ({ values }) => ({
102
        type: 'list',
103
        choices: objectToChoices(values) }),
104
    MultiSelectBox: ({ values }) => ({
105
        type: 'checkbox',
106
        choices: objectToChoices(values) }),
107
    File: ~{ type: 'filePath', basePath: process.cwd() }
108
};
109
110
/**
111
 * Custom autocomplete component
112
 *
113
 * @param {String} name
114
 * @param {String} source
115
 * @return {Function}
116
 */
117
const getAutocompleteComponent = (name, source) => {
118
    if (!source) {
119
        throw new Error(`aren't you missing 'autocomplete/${name}.js'?`);
120
    }
121
122
    return ~{ type: 'autocomplete', source: compileClosure(name, source) };
123
};
124
125
/**
126
 * Converts a Rung CLI question object to an Inquirer question object
127
 *
128
 * @author Marcelo Haskell Camargo
129
 * @param {String[]} sources
130
 * @param {String} name
0 ignored issues
show
The parameter name does not exist. Did you maybe forget to remove this comment?
Loading history...
131
 * @param {Object} config
0 ignored issues
show
The parameter config does not exist. Did you maybe forget to remove this comment?
Loading history...
132
 * @return {Object}
133
 */
134
const toInquirerQuestion = curry((sources, [name, config]) => {
135
    const component = components
136
        | cond([
137
            [~(config.type.name === 'AutoComplete'), ~getAutocompleteComponent(name, sources[name])],
138
            [has(config.type.name), _[config.type.name]],
0 ignored issues
show
The variable _ seems to be never declared. If this is a global, consider adding a /** global: _ */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
139
            [T, _.String]
140
        ]);
141
142
    return merge(config
143
        | renameKeys({ description: 'message' })
144
        | merge(__, { name }), component(config.type));
145
});
146
147
const readFile = promisify(fs.readFile);
148
149
/**
150
 * Opens the provided files and returns them as node buffers
151
 *
152
 * @param {String[]} fields
153
 * @param {Object} answers
0 ignored issues
show
The parameter answers does not exist. Did you maybe forget to remove this comment?
Loading history...
154
 * @return {Promise}
155
 */
156
const openFiles = fields =>
157
    mapObjIndexed((value, param) => value
158
        | when(~contains(param, fields), concat(process.cwd() + '/') & readFile))
159
    & props;
160
161
/**
162
 * Returns the pure JS values from received questions that will be answered
163
 *
164
 * @author Marcelo Haskell Camargo
165
 * @param {Object} questions
166
 * @return {Promise} answers for the questions by key
167
 */
168
export function ask(questions) {
169
    const DatePickerPrompt = require('inquirer-datepicker-prompt');
170
    const ChalkPipe = require('inquirer-chalk-pipe');
171
    const AutocompletePrompt = require('inquirer-autocomplete-prompt');
172
    const FilePath = require('inquirer-file-path');
173
    const fileFields = questions
174
        | filterWhere(pathEq(['type', 'name'], 'File'))
175
        | keys;
176
177
    const prompt = createPromptModule();
178
    prompt.registerPrompt('datetime', DatePickerPrompt);
179
    prompt.registerPrompt('chalk-pipe', ChalkPipe);
180
    prompt.registerPrompt('autocomplete', AutocompletePrompt);
181
    prompt.registerPrompt('filePath', FilePath);
182
    return getAutocompleteSources()
183
        .then(autocompleteSources => questions
184
            | toPairs
185
            | map(toInquirerQuestion(autocompleteSources))
186
            | prompt)
187
        .then(openFiles(fileFields));
188
}
189