@fimbul/wotan
Version:
Pluggable TypeScript and JavaScript linter
326 lines • 12.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseGlobalOptions = exports.GLOBAL_OPTIONS_SPEC = exports.parseArguments = void 0;
const ymir_1 = require("@fimbul/ymir");
const debug = require("debug");
const optparse_1 = require("./optparse");
const utils_1 = require("./utils");
const log = debug('wotan:argparse');
// @internal
function parseArguments(args, globalOptions) {
log('Parsing arguments: %O', args);
if (globalOptions !== undefined)
log('global options: %O', globalOptions);
args = args.map(trimSingleQuotes);
const commandName = args[0];
let command;
let defaults;
switch (commandName) {
case "lint" /* Lint */:
command = parseLintCommand(args.slice(1), defaults = parseGlobalOptions(globalOptions), "lint" /* Lint */);
break;
case "save" /* Save */:
command = parseLintCommand(args.slice(1), defaults = parseGlobalOptions(globalOptions), "save" /* Save */);
break;
default:
command =
parseLintCommand(args, defaults = parseGlobalOptions(globalOptions), "lint" /* Lint */);
break;
case "test" /* Test */:
command = parseTestCommand(args.slice(1));
break;
case "show" /* Show */:
command = parseShowCommand(args.slice(1), defaults = parseGlobalOptions(globalOptions));
break;
case "validate" /* Validate */:
command = parseValidateCommand(args.slice(1));
}
log("Parsed '%s' command as %O", command.command, command);
if (defaults !== undefined)
log('used defaults %O', defaults);
return command;
}
exports.parseArguments = parseArguments;
exports.GLOBAL_OPTIONS_SPEC = {
modules: optparse_1.OptionParser.Transform.withDefault(optparse_1.OptionParser.Factory.parsePrimitiveOrArray('string'), utils_1.emptyArray),
config: optparse_1.OptionParser.Factory.parsePrimitive('string'),
files: optparse_1.OptionParser.Transform.withDefault(optparse_1.OptionParser.Factory.parsePrimitiveOrArray('string'), utils_1.emptyArray),
exclude: optparse_1.OptionParser.Transform.withDefault(optparse_1.OptionParser.Factory.parsePrimitiveOrArray('string'), utils_1.emptyArray),
project: optparse_1.OptionParser.Transform.withDefault(optparse_1.OptionParser.Factory.parsePrimitiveOrArray('string'), utils_1.emptyArray),
references: optparse_1.OptionParser.Transform.withDefault(optparse_1.OptionParser.Factory.parsePrimitive('boolean'), false),
cache: optparse_1.OptionParser.Transform.withDefault(optparse_1.OptionParser.Factory.parsePrimitive('boolean'), false),
formatter: optparse_1.OptionParser.Factory.parsePrimitive('string'),
fix: optparse_1.OptionParser.Transform.withDefault(optparse_1.OptionParser.Factory.parsePrimitive('boolean', 'number'), false),
extensions: optparse_1.OptionParser.Transform.map(optparse_1.OptionParser.Factory.parsePrimitiveOrArray('string'), sanitizeExtensionArgument),
reportUselessDirectives: optparse_1.OptionParser.Transform.transform(optparse_1.OptionParser.Factory.parsePrimitive('string', 'boolean'), (value) => {
switch (value) {
case true:
case false:
case 'error':
case 'warning':
case 'suggestion':
return value;
case 'warn':
return 'warning';
case 'hint':
return 'suggestion';
case undefined:
case 'off':
return false;
default:
return 'error';
}
}),
};
function parseGlobalOptions(options) {
return optparse_1.OptionParser.parse(options, exports.GLOBAL_OPTIONS_SPEC, { context: 'global options' });
}
exports.parseGlobalOptions = parseGlobalOptions;
function parseLintCommand(args, defaults, command) {
const result = {
command,
...defaults,
};
const exclude = [];
const extensions = [];
const modules = [];
const files = [];
const projects = [];
outer: for (let i = 0; i < args.length; ++i) {
const arg = args[i];
switch (arg) {
case '-p':
case '--project':
result.project = projects;
const project = expectStringArgument(args, ++i, arg);
if (project !== '')
projects.push(project);
break;
case '-r':
case '--references':
({ index: i, argument: result.references } = parseOptionalBoolean(args, i));
break;
case '--cache':
({ index: i, argument: result.cache } = parseOptionalBoolean(args, i));
break;
case '-e':
case '--exclude':
result.exclude = exclude;
const opt = expectStringArgument(args, ++i, arg);
if (opt !== '')
exclude.push(opt);
break;
case '-f':
case '--formatter':
result.formatter = expectStringArgument(args, ++i, arg) || undefined;
break;
case '-c':
case '--config':
result.config = expectStringArgument(args, ++i, arg) || undefined;
break;
case '--fix':
({ index: i, argument: result.fix } = parseOptionalBooleanOrNumber(args, i));
break;
case '--ext':
result.extensions = extensions;
extensions.push(...expectStringArgument(args, ++i, arg).split(/,/g).map(sanitizeExtensionArgument).filter(isTruthy));
break;
case '-m':
case '--module':
result.modules = modules;
modules.push(...expectStringArgument(args, ++i, arg).split(/,/g).filter(isTruthy));
break;
case '--report-useless-directives':
({ index: i, argument: result.reportUselessDirectives } = parseOptionalSeverityOrBoolean(args, i));
break;
case '--':
result.files = files;
files.push(...args.slice(i + 1).filter(isTruthy));
break outer;
default:
if (arg.startsWith('-'))
throw new ymir_1.ConfigurationError(`Unknown option '${arg}'.`);
result.files = files;
if (arg !== '')
files.push(arg);
}
}
const usesProject = result.project.length !== 0 || result.files.length === 0;
if (result.extensions !== undefined) {
if (result.extensions.length === 0) {
result.extensions = undefined;
}
else if (usesProject) {
throw new ymir_1.ConfigurationError("Options '--ext' and '--project' cannot be used together.");
}
}
if (result.cache && !usesProject)
throw new ymir_1.ConfigurationError("Option '--cache' can only be used together with '--project'");
return result;
}
function isTruthy(v) {
return v !== '';
}
function sanitizeExtensionArgument(ext) {
ext = ext.trim();
return ext === '' || ext.startsWith('.') ? ext : `.${ext}`;
}
function parseTestCommand(args) {
const modules = [];
const result = {
modules,
command: "test" /* Test */,
bail: false,
files: [],
updateBaselines: false,
exact: false,
};
outer: for (let i = 0; i < args.length; ++i) {
const arg = args[i];
switch (arg) {
case '--exact':
({ index: i, argument: result.exact } = parseOptionalBoolean(args, i));
break;
case '--bail':
({ index: i, argument: result.bail } = parseOptionalBoolean(args, i));
break;
case '-u':
case '--update':
({ index: i, argument: result.updateBaselines } = parseOptionalBoolean(args, i));
break;
case '-m':
case '--module':
modules.push(...expectStringArgument(args, ++i, arg).split(/,/g).filter(isTruthy));
break;
case '--':
result.files.push(...args.slice(i + 1).filter(isTruthy));
break outer;
default:
if (arg.startsWith('-'))
throw new ymir_1.ConfigurationError(`Unknown option '${arg}'.`);
if (arg !== '')
result.files.push(arg);
}
}
if (result.files.length === 0)
throw new ymir_1.ConfigurationError('filename expected.');
return result;
}
function parseShowCommand(args, defaults) {
const files = [];
let modules;
let format;
let config = defaults.config;
outer: for (let i = 0; i < args.length; ++i) {
const arg = args[i];
switch (arg) {
case '-f':
case '--format':
format = expectFormatArgument(args, ++i, arg);
break;
case '-c':
case '--config':
config = expectStringArgument(args, ++i, arg) || undefined;
break;
case '-m':
case '--module':
(modules !== null && modules !== void 0 ? modules : (modules = [])).push(...expectStringArgument(args, ++i, arg).split(/,/g).filter(isTruthy));
break;
case '--':
files.push(...args.slice(i + 1).filter(isTruthy));
break outer;
default:
if (arg.startsWith('-'))
throw new ymir_1.ConfigurationError(`Unknown option '${arg}'.`);
if (arg !== '')
files.push(arg);
}
}
switch (files.length) {
case 0:
throw new ymir_1.ConfigurationError('filename expected');
case 1:
return {
format,
config,
modules: modules !== null && modules !== void 0 ? modules : defaults.modules,
command: "show" /* Show */,
file: files[0],
};
default:
throw new ymir_1.ConfigurationError('more than one filename provided');
}
}
function parseValidateCommand(_args) {
throw new ymir_1.ConfigurationError("'validate' is not implemented yet.");
}
function parseOptionalBooleanOrNumber(args, index) {
if (index + 1 !== args.length) {
switch (args[index + 1]) {
case 'true':
return { index: index + 1, argument: true };
case 'false':
return { index: index + 1, argument: false };
default: {
const num = parseInt(args[index + 1], 10);
if (!Number.isNaN(num))
return { index: index + 1, argument: num };
}
}
}
return { index, argument: true };
}
function parseOptionalSeverityOrBoolean(args, index) {
if (index + 1 !== args.length) {
switch (args[index + 1]) {
case 'true':
return { index: index + 1, argument: true };
case 'false':
case 'off':
return { index: index + 1, argument: false };
case 'error':
return { index: index + 1, argument: 'error' };
case 'warn':
case 'warning':
return { index: index + 1, argument: 'warning' };
case 'hint':
case 'suggestion':
return { index: index + 1, argument: 'suggestion' };
}
}
return { index, argument: true };
}
function parseOptionalBoolean(args, index) {
if (index + 1 !== args.length) {
switch (args[index + 1]) {
case 'true':
return { index: index + 1, argument: true };
case 'false':
return { index: index + 1, argument: false };
}
}
return { index, argument: true };
}
function expectFormatArgument(args, index, opt) {
const arg = expectStringArgument(args, index, opt).toLowerCase();
switch (arg) {
case "json" /* Json */:
case "json5" /* Json5 */:
case "yaml" /* Yaml */:
return arg;
default:
return assertNever(arg, `Argument for option '${opt}' must be one of '${"json" /* Json */}', '${"json5" /* Json5 */}' or '${"yaml" /* Yaml */}'.`);
}
}
function expectStringArgument(args, index, opt) {
if (index === args.length)
throw new ymir_1.ConfigurationError(`Option '${opt}' expects an argument.`);
return args[index];
}
function trimSingleQuotes(str) {
return str.startsWith("'") ? str.slice(1, -1) : str;
}
function assertNever(_, message) {
throw new ymir_1.ConfigurationError(message);
}
//# sourceMappingURL=argparse.js.map
;