"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.consoleLog = exports.ensureCall = exports.normalizeEslintDiagnostic = exports.normalizeVueTscDiagnostic = exports.lspRange2Location = exports.uriToAbsPath = exports.normalizePublishDiagnosticParams = exports.normalizeLspDiagnostic = exports.normalizeTsDiagnostic = exports.composeCheckerSummary = exports.wrapCheckerSummary = exports.tsLocationToBabelLocation = exports.createFrame = exports.toViteCustomPayload = exports.diagnosticToRuntimeError = exports.diagnosticToTerminalLog = exports.filterLogLevel = void 0;
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const strip_ansi_1 = __importDefault(require("strip-ansi"));
const vscode_uri_1 = require("vscode-uri");
const worker_threads_1 = require("worker_threads");
const code_frame_1 = require("@babel/code-frame");
const index_1 = require("./client/index");
const types_1 = require("./types");
const defaultLogLevel = [
    types_1.DiagnosticLevel.Warning,
    types_1.DiagnosticLevel.Error,
    types_1.DiagnosticLevel.Suggestion,
    types_1.DiagnosticLevel.Message,
];
function filterLogLevel(diagnostics, level = defaultLogLevel) {
    if (Array.isArray(diagnostics)) {
        return diagnostics.filter((d) => {
            if (typeof d.level !== 'number')
                return false;
            return level.includes(d.level);
        });
    }
    else {
        if (!diagnostics.level)
            return null;
        return level.includes(diagnostics.level) ? diagnostics : null;
    }
}
exports.filterLogLevel = filterLogLevel;
function diagnosticToTerminalLog(d, name) {
    var _a;
    const nameInLabel = name ? `(${name})` : '';
    const boldBlack = chalk_1.default.bold.rgb(0, 0, 0);
    const labelMap = {
        [types_1.DiagnosticLevel.Error]: boldBlack.bgRedBright(` ERROR${nameInLabel} `),
        [types_1.DiagnosticLevel.Warning]: boldBlack.bgYellowBright(` WARNING${nameInLabel} `),
        [types_1.DiagnosticLevel.Suggestion]: boldBlack.bgBlueBright(` SUGGESTION${nameInLabel} `),
        [types_1.DiagnosticLevel.Message]: boldBlack.bgCyanBright(` MESSAGE${nameInLabel} `),
    };
    const levelLabel = labelMap[(_a = d.level) !== null && _a !== void 0 ? _a : types_1.DiagnosticLevel.Error];
    const fileLabel = boldBlack.bgCyanBright(' FILE ') + ' ';
    const position = d.loc
        ? chalk_1.default.yellow(d.loc.start.line) + ':' + chalk_1.default.yellow(d.loc.start.column)
        : '';
    return [
        levelLabel + ' ' + d.message,
        fileLabel + d.id + ':' + position + os_1.default.EOL,
        d.codeFrame + os_1.default.EOL,
        d.conclusion,
    ]
        .filter(Boolean)
        .join(os_1.default.EOL);
}
exports.diagnosticToTerminalLog = diagnosticToTerminalLog;
function diagnosticToRuntimeError(diagnostics) {
    const diagnosticsArray = Array.isArray(diagnostics) ? diagnostics : [diagnostics];
    const results = diagnosticsArray.map((d) => {
        var _a;
        let loc;
        if (d.loc) {
            loc = {
                file: d.id,
                line: d.loc.start.line,
                column: typeof d.loc.start.column === 'number' ? d.loc.start.column : 0,
            };
        }
        return {
            message: (_a = d.message) !== null && _a !== void 0 ? _a : '',
            stack: typeof d.stack === 'string' ? d.stack : Array.isArray(d.stack) ? d.stack.join(os_1.default.EOL) : '',
            id: d.id,
            frame: d.stripedCodeFrame,
            checkerId: d.checker,
            level: d.level,
            loc,
        };
    });
    return Array.isArray(diagnostics) ? results : results[0];
}
exports.diagnosticToRuntimeError = diagnosticToRuntimeError;
function toViteCustomPayload(id, diagnostics) {
    return {
        type: 'custom',
        event: index_1.WS_CHECKER_ERROR_EVENT,
        data: {
            checkerId: id,
            diagnostics,
        },
    };
}
exports.toViteCustomPayload = toViteCustomPayload;
function createFrame({ source, location, }) {
    const frame = (0, code_frame_1.codeFrameColumns)(source, location, {
        // worker tty did not fork parent process stdout, let's make a workaround
        forceColor: true,
    })
        .split('\n')
        .map((line) => '  ' + line)
        .join(os_1.default.EOL);
    return frame;
}
exports.createFrame = createFrame;
function tsLocationToBabelLocation(tsLoc) {
    return {
        start: { line: tsLoc.start.line + 1, column: tsLoc.start.character + 1 },
        end: { line: tsLoc.end.line + 1, column: tsLoc.end.character + 1 },
    };
}
exports.tsLocationToBabelLocation = tsLocationToBabelLocation;
function wrapCheckerSummary(checkerName, rawSummary) {
    return `[${checkerName}] ${rawSummary}`;
}
exports.wrapCheckerSummary = wrapCheckerSummary;
function composeCheckerSummary(checkerName, errorCount, warningCount) {
    const message = `Found ${errorCount} error${errorCount > 1 ? 's' : ''} and ${warningCount} warning${warningCount > 1 ? 's' : ''}`;
    const hasError = errorCount > 0;
    const hasWarning = warningCount > 0;
    const color = hasError ? 'red' : hasWarning ? 'yellow' : 'green';
    return chalk_1.default[color](wrapCheckerSummary(checkerName, message));
}
exports.composeCheckerSummary = composeCheckerSummary;
/* ------------------------------- TypeScript ------------------------------- */
function normalizeTsDiagnostic(d) {
    var _a, _b, _c, _d;
    const fileName = (_a = d.file) === null || _a === void 0 ? void 0 : _a.fileName;
    const { flattenDiagnosticMessageText, } = require('typescript');
    const message = flattenDiagnosticMessageText(d.messageText, os_1.default.EOL);
    let loc;
    const pos = d.start === undefined ? null : (_b = d.file) === null || _b === void 0 ? void 0 : _b.getLineAndCharacterOfPosition(d.start);
    if (pos && d.file && typeof d.start === 'number' && typeof d.length === 'number') {
        loc = tsLocationToBabelLocation({
            start: (_c = d.file) === null || _c === void 0 ? void 0 : _c.getLineAndCharacterOfPosition(d.start),
            end: (_d = d.file) === null || _d === void 0 ? void 0 : _d.getLineAndCharacterOfPosition(d.start + d.length),
        });
    }
    let codeFrame;
    if (loc) {
        codeFrame = createFrame({
            source: d.file.text,
            location: loc,
        });
    }
    return {
        message,
        conclusion: '',
        codeFrame,
        stripedCodeFrame: codeFrame && (0, strip_ansi_1.default)(codeFrame),
        id: fileName,
        checker: 'TypeScript',
        loc,
        level: d.category,
    };
}
exports.normalizeTsDiagnostic = normalizeTsDiagnostic;
/* ----------------------------------- LSP ---------------------------------- */
function normalizeLspDiagnostic({ diagnostic, absFilePath, fileText, }) {
    let level = types_1.DiagnosticLevel.Error;
    const loc = lspRange2Location(diagnostic.range);
    const codeFrame = (0, code_frame_1.codeFrameColumns)(fileText, loc);
    switch (diagnostic.severity) {
        case 1: // Error
            level = types_1.DiagnosticLevel.Error;
            break;
        case 2: // Warning
            level = types_1.DiagnosticLevel.Warning;
            break;
        case 3: // Information
            level = types_1.DiagnosticLevel.Message;
            break;
        case 4: // Hint
            level = types_1.DiagnosticLevel.Suggestion;
            break;
    }
    return {
        message: diagnostic.message.trim(),
        conclusion: '',
        codeFrame,
        stripedCodeFrame: codeFrame && (0, strip_ansi_1.default)(codeFrame),
        id: absFilePath,
        checker: 'VLS',
        loc,
        level,
    };
}
exports.normalizeLspDiagnostic = normalizeLspDiagnostic;
function normalizePublishDiagnosticParams(publishDiagnostics) {
    return __awaiter(this, void 0, void 0, function* () {
        const diagnostics = publishDiagnostics.diagnostics;
        const absFilePath = uriToAbsPath(publishDiagnostics.uri);
        const { readFile } = fs_1.default.promises;
        const fileText = yield readFile(absFilePath, 'utf-8');
        const res = diagnostics.map((d) => {
            return normalizeLspDiagnostic({
                diagnostic: d,
                absFilePath,
                fileText,
            });
        });
        return res;
    });
}
exports.normalizePublishDiagnosticParams = normalizePublishDiagnosticParams;
function uriToAbsPath(documentUri) {
    return vscode_uri_1.URI.parse(documentUri).fsPath;
}
exports.uriToAbsPath = uriToAbsPath;
function lspRange2Location(range) {
    return {
        start: {
            line: range.start.line + 1,
            column: range.start.character + 1,
        },
        end: {
            line: range.end.line + 1,
            column: range.end.character + 1,
        },
    };
}
exports.lspRange2Location = lspRange2Location;
/* --------------------------------- vue-tsc -------------------------------- */
function normalizeVueTscDiagnostic(d) {
    const diagnostic = normalizeTsDiagnostic(d);
    diagnostic.checker = 'vue-tsc';
    return diagnostic;
}
exports.normalizeVueTscDiagnostic = normalizeVueTscDiagnostic;
/* --------------------------------- ESLint --------------------------------- */
const isNormalizedDiagnostic = (d) => {
    return Boolean(d);
};
function normalizeEslintDiagnostic(diagnostic) {
    return diagnostic.messages
        .map((d) => {
        var _a;
        let level = types_1.DiagnosticLevel.Error;
        switch (d.severity) {
            case 0: // off, ignore this
                level = types_1.DiagnosticLevel.Error;
                return null;
            case 1: // warn
                level = types_1.DiagnosticLevel.Warning;
                break;
            case 2: // error
                level = types_1.DiagnosticLevel.Error;
                break;
        }
        const loc = {
            start: {
                line: d.line,
                column: d.column,
            },
            end: {
                line: d.endLine || 0,
                column: d.endColumn,
            },
        };
        const codeFrame = createFrame({
            source: (_a = diagnostic.source) !== null && _a !== void 0 ? _a : '',
            location: loc,
        });
        return {
            message: d.message,
            conclusion: '',
            codeFrame,
            stripedCodeFrame: codeFrame && (0, strip_ansi_1.default)(codeFrame),
            id: diagnostic.filePath,
            checker: 'ESLint',
            loc,
            level,
        };
    })
        .filter(isNormalizedDiagnostic);
}
exports.normalizeEslintDiagnostic = normalizeEslintDiagnostic;
/* ------------------------------ miscellaneous ----------------------------- */
function ensureCall(callback) {
    setTimeout(() => {
        callback();
    });
}
exports.ensureCall = ensureCall;
function consoleLog(value) {
    if (worker_threads_1.isMainThread) {
        console.log(value);
    }
    else {
        worker_threads_1.parentPort === null || worker_threads_1.parentPort === void 0 ? void 0 : worker_threads_1.parentPort.postMessage({
            type: types_1.ACTION_TYPES.console,
            payload: value,
        });
    }
}
exports.consoleLog = consoleLog;
