UNPKG

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