@maniascript/mslint
Version:
ManiaScript linter
248 lines (244 loc) • 8.94 kB
JavaScript
import path from 'node:path';
import { loadConfig, createConfig } from './config.js';
import { MSLintError } from './error.js';
import { Linter } from './lint.js';
import * as output from './output.js';
import { version } from '../../version.js';
import { Severity } from './rule.js';
import { allRules } from '../rules/index.js';
var Capture;
(function (Capture) {
Capture[Capture["None"] = 0] = "None";
Capture[Capture["ConfigPath"] = 1] = "ConfigPath";
})(Capture || (Capture = {}));
const CLI_HELP = `
Usage: mslint [options] [file|glob-pattern]+
-h, --help Displays this help
-v, --version Displays MSLint version number
-c, --config String Path to an MSLint configuration file - default: ""
-l, --verbose Displays information about the lint as it progresses
-s, --stats Displays lint statistics at the end
--report-unused-disable-directives Report disable directive comments that are not useful
--report-disable-directives-without-description Report disable directive comments without a description
mslint version: ${version}
`;
function formatResult(result) {
let resultOuput = '';
let errorsCount = 0;
let warningsCount = 0;
if (!result.success)
resultOuput += '\n';
for (const fileResult of result.files) {
if (fileResult.messages.length <= 0)
continue;
const messagesOutput = [];
const maxLength = [];
for (const message of fileResult.messages) {
if (message.severity !== Severity.Off) {
let severityOutput = '';
switch (message.severity) {
case Severity.Error: {
severityOutput = output.formatTextColorRed('error');
break;
}
case Severity.Warn: {
severityOutput = output.formatTextColorOrange('warning');
break;
}
}
const messageOuput = [
output.formatTextDim(`${message.source.loc.start.line.toString()}:${message.source.loc.start.column.toString()}`),
severityOutput,
message.message,
output.formatTextDim(message.ruleId ?? '')
];
for (let index = 0; index < messageOuput.length; index++) {
if (maxLength.at(index) === undefined || maxLength[index] < messageOuput[index].length) {
maxLength[index] = messageOuput[index].length;
}
}
if (message.severity === Severity.Error) {
errorsCount++;
}
else {
warningsCount++;
}
messagesOutput.push(messageOuput);
}
}
if (messagesOutput.length > 0) {
resultOuput += `${output.formatTextUnderline(path.resolve(fileResult.path))}\n`;
for (const message of messagesOutput) {
let index = 0;
for (const part of message) {
resultOuput += ` ${part.padEnd(maxLength[index], ' ')}`;
index++;
}
resultOuput += '\n';
}
}
}
if (errorsCount === 1) {
resultOuput += `\n🔴 ${output.formatTextColorRed('1 error')}`;
}
else if (errorsCount > 1) {
resultOuput += `\n🔴 ${output.formatTextColorRed(`${errorsCount.toString()} errors`)}`;
}
if (warningsCount === 1) {
resultOuput += `\n🟡 ${output.formatTextColorOrange('1 warning')}`;
}
else if (warningsCount > 1) {
resultOuput += `\n🟡 ${output.formatTextColorOrange(`${warningsCount.toString()} warnings`)}`;
}
if (errorsCount > 0 || warningsCount > 0) {
resultOuput += '\n';
}
return resultOuput;
}
function captureArgumentValue(command, capture, argName, argValue) {
if (argValue.startsWith('-')) {
throw new MSLintError(`Missing value for argument ${argName}`);
}
switch (capture) {
case Capture.ConfigPath: {
command.configPath = argValue;
break;
}
default: {
throw new MSLintError(`Unknown argument ${argName}. Use "mslint --help" to see all valid arguments.`);
}
}
return command;
}
function parseArguments(args) {
let command = {
configPath: null,
patterns: null,
verbose: null,
displayStats: null,
displayVersion: null,
displayHelp: null,
reportUnusedDisableDirective: null,
reportDisableDirectiveWithoutDescription: null
};
let capture = Capture.None;
let argIndex = 0;
for (const arg of args) {
if (capture !== Capture.None) {
command = captureArgumentValue(command, capture, args[argIndex - 1], arg);
capture = Capture.None;
}
else if (arg.startsWith('-')) {
if (command.patterns !== null) {
throw new MSLintError(`You can't add more arguments after path '${command.patterns.pop() ?? '???'}'`);
}
else if (arg === '--config' || arg === '-c') {
if (command.configPath === null) {
capture = Capture.ConfigPath;
}
else {
throw new MSLintError('You can use [--config, -c] only once');
}
}
else if (arg === '--verbose' || arg === '-l') {
command.verbose = true;
}
else if (arg === '--stats' || arg === '-s') {
command.displayStats = true;
}
else if (arg === '--version' || arg === '-v') {
command.displayVersion = true;
}
else if (arg === '--help' || arg === '-h') {
command.displayHelp = true;
}
else if (arg === '--report-unused-disable-directives') {
command.reportUnusedDisableDirective = true;
}
else if (arg === '--report-disable-directives-without-description') {
command.reportDisableDirectiveWithoutDescription = true;
}
else {
throw new MSLintError(`Unknown argument ${arg}. Use "mslint --help" to see all valid arguments.`);
}
}
else {
if (command.patterns === null) {
command.patterns = [arg];
}
else {
command.patterns.push(arg);
}
}
argIndex += 1;
}
if (capture !== Capture.None) {
throw new MSLintError(`Missing value for last argument ${args[args.length - 1]}`);
}
return command;
}
async function execute(args, cwd) {
const command = parseArguments(args);
if (command.displayHelp === true) {
output.info(CLI_HELP);
return 0;
}
if (command.displayVersion === true) {
output.info(`Version ${version}`);
return 0;
}
// Setup config
let config;
if (command.configPath !== null) {
config = loadConfig(command.configPath);
}
else {
// @Todo Before creating a default config, try to search for a config file `mslint.json` in the current directory or its parent ?
const rules = {};
for (const [ruleId, rule] of allRules) {
rules[ruleId] = ['error', rule.meta.settings];
}
config = createConfig({
cwd,
rules
});
}
const patterns = [];
for (const pattern of config.patterns) {
if (!patterns.includes(pattern)) {
patterns.push(pattern);
}
}
if (command.patterns !== null) {
for (const pattern of command.patterns) {
if (!patterns.includes(pattern)) {
patterns.push(pattern);
}
}
}
if (patterns.length === 0) {
throw new MSLintError('You must provide a glob pattern to lint');
}
if (command.verbose !== null) {
config.verbose = command.verbose;
}
if (command.displayStats !== null) {
config.displayStats = command.displayStats;
}
if (command.reportUnusedDisableDirective !== null) {
config.reportUnusedDisableDirective = command.reportUnusedDisableDirective;
}
if (command.reportDisableDirectiveWithoutDescription !== null) {
config.reportDisableDirectiveWithoutDescription = command.reportDisableDirectiveWithoutDescription;
}
const linter = new Linter(config);
const result = await linter.lint(patterns);
output.info(formatResult(result));
if (result.success) {
return 0;
}
else {
return 1;
}
}
export { parseArguments, execute };