#!/usr/bin/env node /** * JavaScript Sanitizer for Code Quality Checks * * This script removes comments and replaces string contents with spaces * while preserving line numbers and column positions for accurate violation reporting. * * Usage: node js-sanitizer.js * Output: Sanitized JavaScript to stdout */ const fs = require('fs'); const decomment = require('decomment'); const acorn = require('acorn'); // Get input file from command line arguments const inputFile = process.argv[2]; if (!inputFile) { console.error('Usage: node js-sanitizer.js '); process.exit(1); } // Read the input file let code; try { code = fs.readFileSync(inputFile, 'utf8'); } catch (error) { console.error(`Error reading file: ${error.message}`); process.exit(1); } // Step 1: Remove comments while preserving spaces let sanitized; try { sanitized = decomment(code, { space: true, // Replace comments with spaces to preserve line numbers tolerant: true // Handle potential syntax issues }); } catch (error) { // If decomment fails, continue with original code sanitized = code; } // Step 2: Parse the code to find string literals and replace their contents try { // Parse with location tracking const ast = acorn.parse(sanitized, { ecmaVersion: 'latest', sourceType: 'module', locations: true, ranges: true, allowReturnOutsideFunction: true, allowImportExportEverywhere: true, allowAwaitOutsideFunction: true, allowSuperOutsideMethod: true, allowHashBang: true, onComment: () => {} // Ignore comments (already removed) }); // Convert code to array of characters for manipulation const chars = sanitized.split(''); // Function to replace string content with spaces function replaceStringContent(node) { if (node.type === 'Literal' && typeof node.value === 'string') { // For string literals, replace the content (but keep the quotes) const start = node.range[0]; const end = node.range[1]; // Find the quote character (first non-whitespace character) let quoteChar = chars[start]; let quoteEnd = chars[end - 1]; // Replace everything between quotes with spaces for (let i = start + 1; i < end - 1; i++) { if (chars[i] !== '\n') { // Preserve newlines for line counting chars[i] = ' '; } } } else if (node.type === 'TemplateLiteral') { // For template literals, replace the raw text parts for (let quasi of node.quasis) { const start = quasi.range[0]; const end = quasi.range[1]; // Replace content between backticks or ${...} for (let i = start + 1; i < end - 1; i++) { if (chars[i] !== '\n' && chars[i] !== '$' && chars[i] !== '{' && chars[i] !== '}') { chars[i] = ' '; } } } } } // Walk the AST to find all string literals function walk(node) { if (!node) return; // Process current node replaceStringContent(node); // Recursively process all child nodes for (let key in node) { if (key === 'range' || key === 'loc' || key === 'start' || key === 'end') { continue; // Skip location properties } const value = node[key]; if (Array.isArray(value)) { for (let item of value) { if (typeof item === 'object' && item !== null) { walk(item); } } } else if (typeof value === 'object' && value !== null) { walk(value); } } } // Walk the AST to process all strings walk(ast); // Convert back to string sanitized = chars.join(''); } catch (error) { // If parsing fails (e.g., syntax error), we still have comments removed // Just continue with the decommented version } // Output the sanitized code process.stdout.write(sanitized);