UNPKG

8.14 kBJavaScriptView Raw
1"use strict";
2// tslint:disable interface-over-type-literal
3Object.defineProperty(exports, "__esModule", { value: true });
4const deps_1 = require("./deps");
5const m = deps_1.default()
6 .add('errors', () => require('./errors'))
7 .add('util', () => require('./util'));
8let debug;
9try {
10 if (process.env.CLI_FLAGS_DEBUG !== '1')
11 debug = () => { };
12 else
13 // tslint:disable-next-line no-implicit-dependencies
14 debug = require('debug')('@oclif/parser');
15}
16catch (_a) {
17 debug = () => { };
18}
19class Parser {
20 constructor(input) {
21 this.input = input;
22 this.raw = [];
23 const { pickBy } = m.util;
24 this.context = input.context || {};
25 this.argv = input.argv.slice(0);
26 this._setNames();
27 this.booleanFlags = pickBy(input.flags, f => f.type === 'boolean');
28 }
29 parse() {
30 this._debugInput();
31 const findLongFlag = (arg) => {
32 const name = arg.slice(2);
33 if (this.input.flags[name]) {
34 return name;
35 }
36 if (arg.startsWith('--no-')) {
37 const flag = this.booleanFlags[arg.slice(5)];
38 if (flag && flag.allowNo)
39 return flag.name;
40 }
41 };
42 const findShortFlag = (arg) => {
43 return Object.keys(this.input.flags).find(k => this.input.flags[k].char === arg[1]);
44 };
45 const parseFlag = (arg) => {
46 const long = arg.startsWith('--');
47 const name = long ? findLongFlag(arg) : findShortFlag(arg);
48 if (!name) {
49 const i = arg.indexOf('=');
50 if (i !== -1) {
51 const sliced = arg.slice(i + 1);
52 this.argv.unshift(sliced);
53 const equalsParsed = parseFlag(arg.slice(0, i));
54 if (!equalsParsed) {
55 this.argv.shift();
56 }
57 return equalsParsed;
58 }
59 return false;
60 }
61 const flag = this.input.flags[name];
62 if (flag.type === 'option') {
63 let input;
64 if (long || arg.length < 3) {
65 input = this.argv.shift();
66 }
67 else {
68 input = arg.slice(arg[2] === '=' ? 3 : 2);
69 }
70 if (!input) {
71 throw new m.errors.CLIError(`Flag --${name} expects a value`);
72 }
73 this.raw.push({ type: 'flag', flag: flag.name, input });
74 }
75 else {
76 this.raw.push({ type: 'flag', flag: flag.name, input: arg });
77 // push the rest of the short characters back on the stack
78 if (!long && arg.length > 2) {
79 this.argv.unshift(`-${arg.slice(2)}`);
80 }
81 }
82 return true;
83 };
84 let parsingFlags = true;
85 while (this.argv.length) {
86 const input = this.argv.shift();
87 if (parsingFlags && input.startsWith('-')) {
88 // attempt to parse as arg
89 if (this.input['--'] !== false && input === '--') {
90 parsingFlags = false;
91 continue;
92 }
93 if (parseFlag(input)) {
94 continue;
95 }
96 // not actually a flag if it reaches here so parse as an arg
97 }
98 // not a flag, parse as arg
99 const arg = this.input.args[this._argTokens.length];
100 if (arg)
101 arg.input = input;
102 this.raw.push({ type: 'arg', input });
103 }
104 const argv = this._argv();
105 const args = this._args(argv);
106 const flags = this._flags();
107 this._debugOutput(argv, args, flags);
108 return {
109 args,
110 argv,
111 flags,
112 raw: this.raw,
113 };
114 }
115 _args(argv) {
116 const args = {};
117 for (let i = 0; i < this.input.args.length; i++) {
118 const arg = this.input.args[i];
119 args[arg.name] = argv[i];
120 }
121 return args;
122 }
123 _flags() {
124 const flags = {};
125 for (const token of this._flagTokens) {
126 const flag = this.input.flags[token.flag];
127 if (!flag)
128 throw new m.errors.CLIError(`Unexpected flag ${token.flag}`);
129 if (flag.type === 'boolean') {
130 if (token.input === `--no-${flag.name}`) {
131 flags[token.flag] = false;
132 }
133 else {
134 flags[token.flag] = true;
135 }
136 flags[token.flag] = flag.parse(flags[token.flag], this.context);
137 }
138 else {
139 const input = token.input;
140 if (flag.options && !flag.options.includes(input)) {
141 throw new m.errors.FlagInvalidOptionError(flag, input);
142 }
143 const value = flag.parse ? flag.parse(input, this.context) : input;
144 if (flag.multiple) {
145 flags[token.flag] = flags[token.flag] || [];
146 flags[token.flag].push(value);
147 }
148 else {
149 flags[token.flag] = value;
150 }
151 }
152 }
153 for (const k of Object.keys(this.input.flags)) {
154 const flag = this.input.flags[k];
155 if (flags[k] || flag.type !== 'option')
156 continue;
157 if (flag.env) {
158 let input = process.env[flag.env];
159 if (input)
160 flags[k] = flag.parse(input, this.context);
161 }
162 if (!flags[k] && flag.default) {
163 if (typeof flag.default === 'function') {
164 flags[k] = flag.default(Object.assign({ options: flag, flags }, this.context));
165 }
166 else {
167 flags[k] = flag.default;
168 }
169 }
170 }
171 return flags;
172 }
173 _argv() {
174 const args = [];
175 const tokens = this._argTokens;
176 for (let i = 0; i < Math.max(this.input.args.length, tokens.length); i++) {
177 const token = tokens[i];
178 const arg = this.input.args[i];
179 if (token) {
180 if (arg) {
181 if (arg.options && !arg.options.includes(token.input)) {
182 throw new m.errors.ArgInvalidOptionError(arg, token.input);
183 }
184 args[i] = arg.parse(token.input);
185 }
186 else {
187 args[i] = token.input;
188 }
189 }
190 else {
191 if (arg.default) {
192 if (typeof arg.default === 'function') {
193 args[i] = arg.default();
194 }
195 else {
196 args[i] = arg.default;
197 }
198 }
199 }
200 }
201 return args;
202 }
203 _debugOutput(args, flags, argv) {
204 if (argv.length) {
205 debug('argv: %o', argv);
206 }
207 if (Object.keys(args).length) {
208 debug('args: %o', args);
209 }
210 if (Object.keys(flags).length) {
211 debug('flags: %o', flags);
212 }
213 }
214 _debugInput() {
215 debug('input: %s', this.argv.join(' '));
216 if (this.input.args.length) {
217 debug('available args: %s', this.input.args.map(a => a.name).join(' '));
218 }
219 if (!Object.keys(this.input.flags).length)
220 return;
221 debug('available flags: %s', Object.keys(this.input.flags)
222 .map(f => `--${f}`)
223 .join(' '));
224 }
225 get _argTokens() {
226 return this.raw.filter(o => o.type === 'arg');
227 }
228 get _flagTokens() {
229 return this.raw.filter(o => o.type === 'flag');
230 }
231 _setNames() {
232 for (const k of Object.keys(this.input.flags)) {
233 this.input.flags[k].name = k;
234 }
235 }
236}
237exports.Parser = Parser;