UNPKG

@fimbul/wotan

Version:

Pluggable TypeScript and JavaScript linter

326 lines 12.9 kB
"use strict"; 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