#!/usr/bin/env node const stylelint = require('stylelint') const {readFileSync, writeFileSync} = require('fs') const files = process.argv.slice(2) if (files.length === 0) { files.push('src/**/*.scss') } // we use an empty "marker" to delineate removed lines const REMOVED = `===REMOVED@${Date.now()}===` stylelint.lint({files, reportNeedlessDisables: true}).then(({needlessDisables}) => { for (const {source, ranges} of needlessDisables) { const lines = readFileSync(source, 'utf8').split(/\n/) let offset = 0 for (const {start, unusedRule} of ranges) { let index = start - 1 let line = lines[index] let disable = parseDisableComment(line) if (!disable) { console.warn(`unable to parse disable on line ${start}: ${lines[start - 1]}; trying previous line...`) index-- line = lines[index] disable = parseDisableComment(line) if (!disable) { console.warn(`unable to parse disable on line ${index + 1}: ${lines}`) continue } } const rules = new Set(disable.rules) rules.delete(unusedRule) if (rules.size === 0) { console.log(`- ${line}`) lines[index] = REMOVED } else { const replacement = line.replace(disable.content, `${disable.type} ${Array.from(rules).join(', ')}`) lines[index] = replacement console.log(`- ${line}`) console.log(`+ ${replacement}`) } } const output = lines.filter(line => line !== REMOVED).join('\n') writeFileSync(source, output, 'utf8') } }) function parseDisableComment(str) { const match = str.match(/(stylelint-disable((-next)?-line)?)\s+(.+)$/) return match ? { content: match[0], type: match[1], rules: match[4].split(/,\s+/) } : false }