JS Comment Remover + Syntax Check

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

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Advertisement:

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

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;
            }
        }
    });
})();