UNPKG

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