2 // Copyright (c) 2019 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License as
6 // published by the Free Software Foundation; either version 2 of
7 // the License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
26 constructor(needArgs, helpText, versionText) {
27 needArgs.forEach(name => {
28 if (!name.match(/^(-[^-]|--[^=]+)$/)) {
29 throw new Error(`invalid option name "${name}"`);
32 this.needArgs = needArgs;
33 this.helpText = helpText;
34 this.versionText = versionText;
37 posixlyCorrect() { // eslint-disable-line class-methods-use-this
38 return process.env['POSIXLY_CORRECT'] != null;
42 return this.needArgs.includes(name);
45 fallback(name, params) {
46 if (name === '--excstk') {
48 } else if (name === '--help' && this.helpText != null) {
49 process.stdout.write(this.helpText);
51 } else if (name === '--version' && this.versionText != null) {
52 process.stdout.write(this.versionText);
55 let suffix = this.needsArg(name) ? ' (taking an argument?)' : '';
57 suffix += (this.helpText != null) ? ', try --help' : '';
58 throw new Error(`unknown option "${name}"${suffix}`);
62 reader(args, skip = 2) {
63 return new Options.Reader(this, args, skip);
68 Options.Reader = class {
69 constructor(options, args, skip) {
70 this.options = options;
78 for (optind = this.skip; optind < this.args.length; optind++) {
79 let arg = this.args[optind];
81 if (arg === '-' || !arg.startsWith('-')) {
82 if (this.options.posixlyCorrect()) {
86 } else if (arg === '--') {
92 if (!arg.startsWith('--')) {
94 name = arg.substring(0, 2);
95 value = (name !== arg) ? arg.substring(2) : null;
97 if (this.options.needsArg(name) || value == null) {
100 callback(name, null);
103 } else if (arg.indexOf('=') >= 3) {
104 name = arg.split('=', 1)[0];
105 if (!this.options.needsArg(name)) {
106 throw new Error(`option "${name}" does not take an argument`);
108 value = arg.substring(name.length + 1);
114 if (value === null && Number(this.options.needsArg(name)) > 0) {
115 if (++optind === this.args.length) {
116 throw new Error(`option "${name}" requires an argument`);
118 value = this.args[optind];
120 callback(name, value);
124 this.args.slice(optind).forEach(value => callback(null, value));
128 Object.defineProperty(Options, 'Reader', { 'enumerable': false });
129 Object.defineProperty(Options.Reader, 'name', { value: 'Reader' });
132 function start(programName, options, params, mainProgram) { // eslint-disable-line consistent-return
133 let parsed = (params != null) ? params : new Params();
136 const version = process.version.match(/^v?(\d+)\.(\d+)/);
138 if (version.length < 3) {
139 throw new Error('unable to obtain node version');
140 } else if ((parseInt(version[1]) * 1000 + parseInt(version[2])) < 6009) {
141 throw new Error('node version 6.9.0 or later required');
144 if (params == null) {
145 return mainProgram(options.reader(process.argv), name => options.fallback(name, parsed));
149 options.reader(process.argv).forEach((name, value) => {
153 options.parse(name, value, parsed);
156 return mainProgram(nonopt, parsed);
162 process.stderr.write(`${process.argv.length >= 2 ? process.argv[1] : programName}: ${e.message}\n`);
169 module.exports = Object.freeze({