JS Comment Remover + Syntax Check

Remove comments from JS code, show syntax errors with line numbers (EN/TR/DE)

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Advertisement:

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

Advertisement:

// ==UserScript==
// @name         JS Comment Remover + Syntax Check
// @name:tr      JS Yorum Temizleyici + Sözdizimi Denetimi
// @name:de      JS Kommentar-Entferner + Syntax-Prüfung
// @description  Remove comments from JS code, show syntax errors with line numbers (EN/TR/DE)
// @description:tr JS kodundaki yorumları siler, sözdizimi hatasını satır numarasıyla gösterir (EN/TR/DE)
// @description:de Entfernt Kommentare aus JS-Code, zeigt Syntaxfehler mit Zeilennummer (EN/TR/DE)
// @namespace    https://tampermonkey.net/
// @version      4.0
// @author       Cleaner
// @match        *://*/*
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // Dil sözlüğü (kısa)
    const lang = {
        en: {
            title: "JS Cleaner",
            inputLabel: "Your code (with comments)",
            outputLabel: "Clean code (no comments)",
            placeholder: "Paste JavaScript code here...",
            cleanBtn: "Remove comments",
            copyBtn: "Copy",
            clearBtn: "Clear",
            syntaxError: "Syntax error",
            line: "Line",
            copied: "Copied!",
            noCode: "No code to copy"
        },
        tr: {
            title: "JS Temizleyici",
            inputLabel: "Kodunuz (yorumlu)",
            outputLabel: "Temiz kod (yorumsuz)",
            placeholder: "JavaScript kodunu yapıştır...",
            cleanBtn: "Yorumları sil",
            copyBtn: "Kopyala",
            clearBtn: "Temizle",
            syntaxError: "Sözdizimi hatası",
            line: "Satır",
            copied: "Kopyalandı!",
            noCode: "Kopyalanacak kod yok"
        },
        de: {
            title: "JS Reiniger",
            inputLabel: "Ihr Code (mit Kommentaren)",
            outputLabel: "Sauberer Code (ohne Kommentare)",
            placeholder: "JavaScript-Code einfügen...",
            cleanBtn: "Kommentare entfernen",
            copyBtn: "Kopieren",
            clearBtn: "Löschen",
            syntaxError: "Syntaxfehler",
            line: "Zeile",
            copied: "Kopiert!",
            noCode: "Kein Code zum Kopieren"
        }
    };

    let currentLang = 'en'; // varsayılan

    // Still (pastel çocuk, ama sade)
    GM_addStyle(`
        .js-cleaner-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(252, 245, 235, 0.97);
            backdrop-filter: blur(4px);
            z-index: 999999;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Segoe UI', 'Comic Neue', monospace;
        }
        .js-cleaner-container {
            width: 90vw;
            height: 85vh;
            background: #fffdf5;
            border-radius: 40px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            display: flex;
            flex-direction: row;
            overflow: hidden;
            border: 3px solid #f5d7b0;
        }
        .js-cleaner-pane {
            flex: 1;
            display: flex;
            flex-direction: column;
            padding: 20px;
            gap: 12px;
        }
        .left { background: #fff8ef; border-right: 2px dotted #f5d7b0; }
        .right { background: #fafaf0; }
        .title-bar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            font-weight: bold;
            color: #b47c48;
        }
        .lang-select {
            background: #fef0e0;
            border: 1px solid #f5d7b0;
            border-radius: 30px;
            padding: 4px 10px;
            font-family: monospace;
            cursor: pointer;
        }
        .code-area {
            flex: 1;
            display: flex;
            gap: 6px;
            background: #fffef7;
            border-radius: 30px;
            border: 1px solid #f0dbc0;
            overflow: auto;
        }
        .line-numbers {
            background: #f9efdf;
            padding: 12px 6px;
            text-align: right;
            font-family: monospace;
            font-size: 12px;
            color: #b68b54;
            user-select: none;
            border-right: 1px solid #eedbc8;
            min-width: 40px;
            white-space: pre;
            line-height: 1.5;
        }
        textarea, pre {
            flex: 1;
            padding: 12px;
            font-family: 'Fira Code', monospace;
            font-size: 13px;
            line-height: 1.5;
            background: transparent;
            border: none;
            resize: none;
            outline: none;
            color: #3a5e3f;
        }
        pre {
            margin: 0;
            white-space: pre-wrap;
        }
        .error-box {
            background: #ffe8e6;
            border-left: 8px solid #f3a683;
            padding: 6px 12px;
            border-radius: 30px;
            color: #bc6f4b;
            font-size: 12px;
            font-family: monospace;
        }
        button {
            background: #f9efdf;
            border: 1px solid #f5d7b0;
            padding: 6px 16px;
            border-radius: 40px;
            cursor: pointer;
            font-family: monospace;
            font-weight: bold;
            color: #8b6946;
        }
        button.primary {
            background: #ffe4c4;
            border-color: #e6bc8b;
        }
        button:hover {
            background: #ffe0c0;
        }
        .toast {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: #d9e3ce;
            color: #4a5b4c;
            padding: 8px 18px;
            border-radius: 40px;
            opacity: 0;
            transition: 0.2s;
            pointer-events: none;
            font-size: 13px;
        }
        @media (max-width: 700px) {
            .js-cleaner-container { flex-direction: column; }
            .left { border-right: none; border-bottom: 2px dotted #f5d7b0; }
        }
    `);

    // Yorum temizleme (gelişmiş - tüm satırı siler)
    function stripComments(code) {
        const strings = [];
        const stringRegex = /(['"`])(?:(?!\1|\\)[\s\S]|\\.)*\1/g;
        let processed = code.replace(stringRegex, (m) => {
            strings.push(m);
            return `__JS_STR_${strings.length-1}__`;
        });

        const regexes = [];
        const regexLiteralRegex = /\/(?![*\/])(?:[^\/\\\n\r]|\\.)*\/[gimyus]*/g;
        processed = processed.replace(regexLiteralRegex, (m) => {
            regexes.push(m);
            return `__JS_REGEX_${regexes.length-1}__`;
        });

        // Tek satır yorum: satır başındaki boşluk/tab + // + her şey -> tüm satır silinir
        let noComments = processed.replace(/^\s*\/\/.*$/gm, '');
        // Çok satırlı yorum
        noComments = noComments.replace(/\/\*[\s\S]*?\*\//g, '');

        noComments = noComments.replace(/__JS_REGEX_(\d+)__/g, (_, i) => regexes[parseInt(i)]);
        noComments = noComments.replace(/__JS_STR_(\d+)__/g, (_, i) => strings[parseInt(i)]);

        // Boş satırları temizle (isteğe bağlı)
        return noComments.replace(/^\s*[\r\n]+/gm, '');
    }

    // Syntax kontrolü
    function checkSyntax(code) {
        try {
            new Function(code);
            return { ok: true };
        } catch (err) {
            let line = null;
            let match = err.message.match(/line (\d+)/i);
            if (match) line = parseInt(match[1]);
            if (!line && err.stack) {
                let m = err.stack.match(/<anonymous>:(\d+):/);
                if (m) line = parseInt(m[1]);
            }
            return { ok: false, error: err.message, line };
        }
    }

    // Satır numarası üret
    function lineNumbers(text) {
        if (!text) return '';
        const lines = text.split('\n');
        let out = '';
        for (let i = 1; i <= lines.length; i++) out += i + '\n';
        return out;
    }

    let panel = null;
    let currentTrans = lang.en;

    function updateTexts() {
        currentTrans = lang[currentLang];
        const elements = {
            title: document.querySelector('#panelTitle'),
            leftLabel: document.querySelector('#leftLabel'),
            rightLabel: document.querySelector('#rightLabel'),
            input: document.querySelector('#inputCode'),
            cleanBtn: document.querySelector('#cleanBtn'),
            clearBtn: document.querySelector('#clearBtn'),
            copyBtn: document.querySelector('#copyBtn')
        };
        if (elements.title) elements.title.innerText = currentTrans.title;
        if (elements.leftLabel) elements.leftLabel.innerText = currentTrans.inputLabel;
        if (elements.rightLabel) elements.rightLabel.innerText = currentTrans.outputLabel;
        if (elements.input) elements.input.placeholder = currentTrans.placeholder;
        if (elements.cleanBtn) elements.cleanBtn.innerText = currentTrans.cleanBtn;
        if (elements.clearBtn) elements.clearBtn.innerText = currentTrans.clearBtn;
        if (elements.copyBtn) elements.copyBtn.innerText = currentTrans.copyBtn;
        
        // Hata mesajı varsa güncelle
        const errBox = document.querySelector('#errorBox');
        if (errBox && errBox.innerText) {
            // içerik değişebilir, ama dil değişince resetlemek daha iyi
            validate();
        }
    }

    function showToast(msg) {
        let t = document.querySelector('.toast');
        if (!t) {
            t = document.createElement('div');
            t.className = 'toast';
            document.body.appendChild(t);
        }
        t.textContent = msg;
        t.style.opacity = '1';
        setTimeout(() => t.style.opacity = '0', 1500);
    }

    function copyText(text) {
        GM_setClipboard(text, 'text');
        showToast(currentTrans.copied);
    }

    function validate() {
        const input = document.querySelector('#inputCode');
        const errBox = document.querySelector('#errorBox');
        if (!input) return;
        const code = input.value;
        if (code.trim() === '') {
            errBox.style.display = 'none';
            return;
        }
        const res = checkSyntax(code);
        if (!res.ok) {
            errBox.style.display = 'block';
            const lineInfo = res.line ? ` (${currentTrans.line} ${res.line})` : '';
            errBox.innerHTML = `${currentTrans.syntaxError}${lineInfo}: ${res.error}`;
        } else {
            errBox.style.display = 'none';
        }
    }

    function cleanAndShow() {
        const input = document.querySelector('#inputCode');
        const output = document.querySelector('#outputCode');
        const code = input.value;
        if (code.trim() === '') {
            output.innerText = '';
            document.querySelector('#rightLineNumbers').textContent = '';
            return;
        }
        const cleaned = stripComments(code);
        output.innerText = cleaned;
        document.querySelector('#rightLineNumbers').textContent = lineNumbers(cleaned);
    }

    function createPanel() {
        if (panel) return;
        const overlay = document.createElement('div');
        overlay.className = 'js-cleaner-overlay';
        overlay.innerHTML = `
            <div class="js-cleaner-container">
                <div class="js-cleaner-pane left">
                    <div class="title-bar">
                        <span id="leftLabel">${currentTrans.inputLabel}</span>
                        <select id="langSelect" class="lang-select">
                            <option value="en" ${currentLang==='en'?'selected':''}>🇬🇧 English</option>
                            <option value="tr" ${currentLang==='tr'?'selected':''}>🇹🇷 Türkçe</option>
                            <option value="de" ${currentLang==='de'?'selected':''}>🇩🇪 Deutsch</option>
                        </select>
                    </div>
                    <div class="code-area">
                        <div class="line-numbers" id="leftLineNumbers"></div>
                        <textarea id="inputCode" placeholder="${currentTrans.placeholder}"></textarea>
                    </div>
                    <div id="errorBox" class="error-box" style="display:none;"></div>
                    <div style="display:flex; gap:10px; justify-content:flex-end;">
                        <button id="clearBtn">${currentTrans.clearBtn}</button>
                        <button id="cleanBtn" class="primary">${currentTrans.cleanBtn}</button>
                    </div>
                </div>
                <div class="js-cleaner-pane right">
                    <div class="title-bar">
                        <span id="rightLabel">${currentTrans.outputLabel}</span>
                        <button id="copyBtn">${currentTrans.copyBtn}</button>
                    </div>
                    <div class="code-area">
                        <div class="line-numbers" id="rightLineNumbers"></div>
                        <pre id="outputCode"></pre>
                    </div>
                </div>
            </div>
        `;
        document.body.appendChild(overlay);
        panel = overlay;

        const input = overlay.querySelector('#inputCode');
        const output = overlay.querySelector('#outputCode');
        const leftLines = overlay.querySelector('#leftLineNumbers');
        const rightLines = overlay.querySelector('#rightLineNumbers');
        const cleanBtn = overlay.querySelector('#cleanBtn');
        const clearBtn = overlay.querySelector('#clearBtn');
        const copyBtn = overlay.querySelector('#copyBtn');
        const langSelect = overlay.querySelector('#langSelect');
        const errBox = overlay.querySelector('#errorBox');

        function updateLeftLines() {
            leftLines.textContent = lineNumbers(input.value);
        }

        input.addEventListener('input', () => {
            updateLeftLines();
            clearTimeout(window.validateTimer);
            window.validateTimer = setTimeout(validate, 300);
        });

        cleanBtn.addEventListener('click', () => {
            cleanAndShow();
        });

        clearBtn.addEventListener('click', () => {
            input.value = '';
            output.innerText = '';
            leftLines.textContent = '';
            rightLines.textContent = '';
            errBox.style.display = 'none';
            updateLeftLines();
        });

        copyBtn.addEventListener('click', () => {
            const cleanCode = output.innerText;
            if (cleanCode.trim()) copyText(cleanCode);
            else showToast(currentTrans.noCode);
        });

        langSelect.addEventListener('change', (e) => {
            currentLang = e.target.value;
            updateTexts();
            validate(); // hatayı yeniden göster (dil değişince)
            cleanAndShow(); // çıktıyı yenile (ama içerik aynı)
        });

        updateLeftLines();
        input.focus();
    }

    document.addEventListener('keydown', (e) => {
        if (e.ctrlKey && e.shiftKey && e.key === 'Y') {
            e.preventDefault();
            if (!panel) createPanel();
            else {
                panel.remove();
                panel = null;
            }
        }
    });
})();