Remove comments from JS code, show syntax errors with line numbers (EN/TR/DE)
// ==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;
}
}
});
})();