Passed
Push — master ( db8b6d...8ae6ad )
by Lucas
02:30
created

module.exports   F

Complexity

Conditions 34
Paths 832

Size

Total Lines 148
Code Lines 90

Duplication

Lines 1
Ratio 0.68 %

Importance

Changes 0
Metric Value
cc 34
eloc 90
nc 832
nop 2
dl 1
loc 148
rs 0
c 0
b 0
f 0

4 Functions

Rating   Name   Duplication   Size   Complexity  
A 0 8 1
A 0 10 3
A 0 3 1
A 0 3 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like module.exports often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1 View Code Duplication
module.exports = function (args, opts) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2
    if (!opts) opts = {};
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
3
    
4
    var flags = { bools : {}, strings : {} };
5
    
6
    [].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
7
        flags.bools[key] = true;
8
    });
9
    
10
    [].concat(opts.string).filter(Boolean).forEach(function (key) {
11
        flags.strings[key] = true;
12
    });
13
    
14
    var aliases = {};
15
    Object.keys(opts.alias || {}).forEach(function (key) {
16
        aliases[key] = [].concat(opts.alias[key]);
17
        aliases[key].forEach(function (x) {
18
            aliases[x] = [key].concat(aliases[key].filter(function (y) {
19
                return x !== y;
20
            }));
21
        });
22
    });
23
    
24
    var defaults = opts['default'] || {};
25
    
26
    var argv = { _ : [] };
27
    Object.keys(flags.bools).forEach(function (key) {
28
        setArg(key, defaults[key] === undefined ? false : defaults[key]);
29
    });
30
    
31
    var notFlags = [];
32
33
    if (args.indexOf('--') !== -1) {
34
        notFlags = args.slice(args.indexOf('--')+1);
35
        args = args.slice(0, args.indexOf('--'));
36
    }
37
38
    function setArg (key, val) {
39
        var value = !flags.strings[key] && isNumber(val)
40
            ? Number(val) : val
41
        ;
42
        setKey(argv, key.split('.'), value);
43
        
44
        (aliases[key] || []).forEach(function (x) {
45
            setKey(argv, x.split('.'), value);
46
        });
47
    }
48
    
49
    for (var i = 0; i < args.length; i++) {
50
        var arg = args[i];
51
        
52
        if (/^--.+=/.test(arg)) {
53
            // Using [\s\S] instead of . because js doesn't support the
54
            // 'dotall' regex modifier. See:
55
            // http://stackoverflow.com/a/1068308/13216
56
            var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
57
            setArg(m[1], m[2]);
58
        }
59
        else if (/^--no-.+/.test(arg)) {
60
            var key = arg.match(/^--no-(.+)/)[1];
61
            setArg(key, false);
62
        }
63
        else if (/^--.+/.test(arg)) {
64
            var key = arg.match(/^--(.+)/)[1];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable key already seems to be declared on line 60. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
65
            var next = args[i + 1];
66
            if (next !== undefined && !/^-/.test(next)
67
            && !flags.bools[key]
68
            && (aliases[key] ? !flags.bools[aliases[key]] : true)) {
69
                setArg(key, next);
70
                i++;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
71
            }
72
            else if (/^(true|false)$/.test(next)) {
73
                setArg(key, next === 'true');
74
                i++;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
75
            }
76
            else {
77
                setArg(key, flags.strings[key] ? '' : true);
78
            }
79
        }
80
        else if (/^-[^-]+/.test(arg)) {
81
            var letters = arg.slice(1,-1).split('');
82
            
83
            var broken = false;
84
            for (var j = 0; j < letters.length; j++) {
85
                var next = arg.slice(j+2);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable next already seems to be declared on line 65. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
86
                
87
                if (next === '-') {
88
                    setArg(letters[j], next)
89
                    continue;
90
                }
91
                
92
                if (/[A-Za-z]/.test(letters[j])
93
                && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
94
                    setArg(letters[j], next);
95
                    broken = true;
96
                    break;
97
                }
98
                
99
                if (letters[j+1] && letters[j+1].match(/\W/)) {
100
                    setArg(letters[j], arg.slice(j+2));
101
                    broken = true;
102
                    break;
103
                }
104
                else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
105
                    setArg(letters[j], flags.strings[letters[j]] ? '' : true);
106
                }
107
            }
108
            
109
            var key = arg.slice(-1)[0];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable key already seems to be declared on line 60. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
110
            if (!broken && key !== '-') {
111
                if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
112
                && !flags.bools[key]
113
                && (aliases[key] ? !flags.bools[aliases[key]] : true)) {
114
                    setArg(key, args[i+1]);
115
                    i++;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
116
                }
117
                else if (args[i+1] && /true|false/.test(args[i+1])) {
118
                    setArg(key, args[i+1] === 'true');
119
                    i++;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
120
                }
121
                else {
122
                    setArg(key, flags.strings[key] ? '' : true);
123
                }
124
            }
125
        }
126
        else {
127
            argv._.push(
128
                flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
129
            );
130
        }
131
    }
132
    
133
    Object.keys(defaults).forEach(function (key) {
134
        if (!hasKey(argv, key.split('.'))) {
135
            setKey(argv, key.split('.'), defaults[key]);
136
            
137
            (aliases[key] || []).forEach(function (x) {
138
                setKey(argv, x.split('.'), defaults[key]);
139
            });
140
        }
141
    });
142
    
143
    notFlags.forEach(function(key) {
144
        argv._.push(key);
145
    });
146
147
    return argv;
148
};
149
150
function hasKey (obj, keys) {
151
    var o = obj;
152
    keys.slice(0,-1).forEach(function (key) {
153
        o = (o[key] || {});
154
    });
155
156
    var key = keys[keys.length - 1];
157
    return key in o;
158
}
159
160
function setKey (obj, keys, value) {
161
    var o = obj;
162
    keys.slice(0,-1).forEach(function (key) {
163
        if (o[key] === undefined) o[key] = {};
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
164
        o = o[key];
165
    });
166
    
167
    var key = keys[keys.length - 1];
168
    if (o[key] === undefined || typeof o[key] === 'boolean') {
169
        o[key] = value;
170
    }
171
    else if (Array.isArray(o[key])) {
172
        o[key].push(value);
173
    }
174
    else {
175
        o[key] = [ o[key], value ];
176
    }
177
}
178
179
function isNumber (x) {
180
    if (typeof x === 'number') return true;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
181
    if (/^0x[0-9a-f]+$/i.test(x)) return true;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
182
    return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
183
}
184
185
function longest (xs) {
186
    return Math.max.apply(null, xs.map(function (x) { return x.length }));
187
}
188