File indexing completed on 2024-05-12 05:51:45

0001 /**
0002  *  SPDX-FileCopyrightText: 2023 Waqar Ahmed <waqar.17a@gmail.com>
0003  *  SPDX-License-Identifier: LGPL-2.0-or-later
0004  */
0005 #include "eslint.h"
0006 #include "kateproject.h"
0007 
0008 #include <KLocalizedString>
0009 
0010 #include <QJsonArray>
0011 #include <QJsonDocument>
0012 #include <QJsonObject>
0013 
0014 ESLint::ESLint(QObject *parent)
0015     : KateProjectCodeAnalysisTool(parent)
0016 {
0017 }
0018 
0019 QString ESLint::name() const
0020 {
0021     return i18n("ESLint");
0022 }
0023 
0024 QString ESLint::description() const
0025 {
0026     return i18n("ESLint is a static analysis tool & style guide enforcer for JavaScript/Typescript code.");
0027 }
0028 
0029 QString ESLint::fileExtensions() const
0030 {
0031     return QStringLiteral("js|jsx|ts|tsx");
0032 }
0033 
0034 QStringList ESLint::filter(const QStringList &files) const
0035 {
0036     // js/ts files
0037     return files.filter(
0038         QRegularExpression(QStringLiteral("\\.(") + fileExtensions().replace(QStringLiteral("+"), QStringLiteral("\\+")) + QStringLiteral(")$")));
0039 }
0040 
0041 QString ESLint::path() const
0042 {
0043     return QStringLiteral("npx");
0044 }
0045 
0046 QStringList ESLint::arguments()
0047 {
0048     if (!m_project) {
0049         return {};
0050     }
0051 
0052     QStringList args{
0053         QStringLiteral("eslint"),
0054         QStringLiteral("-f"),
0055         QStringLiteral("json"),
0056     };
0057 
0058     const QStringList fileList = filter(m_project->files());
0059     setActualFilesCount(fileList.size());
0060     return args << fileList;
0061 }
0062 
0063 QString ESLint::notInstalledMessage() const
0064 {
0065     return i18n("Please install 'eslint'.");
0066 }
0067 
0068 FileDiagnostics ESLint::parseLine(const QString &line) const
0069 {
0070     QJsonParseError e;
0071     QJsonDocument doc = QJsonDocument::fromJson(line.toUtf8(), &e);
0072     if (e.error != QJsonParseError::NoError) {
0073         return {};
0074     }
0075 
0076     const auto arr = doc.array();
0077     if (arr.empty()) {
0078         return {};
0079     }
0080     auto obj = arr.at(0).toObject();
0081     QUrl uri = QUrl::fromLocalFile(obj.value(QStringLiteral("filePath")).toString());
0082     if (!uri.isValid()) {
0083         return {};
0084     }
0085     const auto messages = obj.value(QStringLiteral("messages")).toArray();
0086     if (messages.empty()) {
0087         return {};
0088     }
0089 
0090     QList<Diagnostic> diags;
0091     diags.reserve(messages.size());
0092     for (const auto &m : messages) {
0093         const auto msg = m.toObject();
0094         if (msg.isEmpty()) {
0095             continue;
0096         }
0097         const int startLine = msg.value(QStringLiteral("line")).toInt() - 1;
0098         const int startColumn = msg.value(QStringLiteral("column")).toInt() - 1;
0099         const int endLine = msg.value(QStringLiteral("endLine")).toInt() - 1;
0100         const int endColumn = msg.value(QStringLiteral("endColumn")).toInt() - 1;
0101         Diagnostic diag;
0102         diag.range = {startLine, startColumn, endLine, endColumn};
0103         if (!diag.range.isValid()) {
0104             continue;
0105         }
0106         diag.code = msg.value(QStringLiteral("ruleId")).toString();
0107         diag.message = msg.value(QStringLiteral("message")).toString();
0108         diag.source = QStringLiteral("eslint");
0109         const int severity = msg.value(QStringLiteral("severity")).toInt();
0110         if (severity == 1) {
0111             diag.severity = DiagnosticSeverity::Warning;
0112         } else if (severity == 2) {
0113             diag.severity = DiagnosticSeverity::Error;
0114         } else {
0115             // fallback, even though there is no other severity
0116             diag.severity = DiagnosticSeverity::Information;
0117         }
0118         diags << diag;
0119     }
0120     return {uri, diags};
0121 }
0122 
0123 QString ESLint::stdinMessages()
0124 {
0125     return QString();
0126 }