Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 2x 3x 3x 17x 1x 16x 16x 16x 16x 16x 16x 21x 4x 2x 2x 2x 16x 16x 3x 3x 3x 3x 3x 6x 6x 9x 9x 9x 6x 3x 3x 3x 6x 6x 3x 3x 3x 9x 2x 8x | const TapMap = require('tap-map') const globby = require('globby') const matchAll = require('string.prototype.matchall') const stylelint = require('stylelint') const {readFileSync} = require('fs') const ruleName = 'primer/no-unused-vars' const cwd = process.cwd() const COLON = ':' const SCSS_VARIABLE_PATTERN = /(\$[-\w]+)/g const messages = stylelint.utils.ruleMessages(ruleName, { rejected: name => `The variable "${name}" is not referenced.` }) const cache = new TapMap() module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}) => { if (!enabled) { return noop } const {files = ['**/*.scss', '!node_modules'], variablePattern = SCSS_VARIABLE_PATTERN, verbose = false} = options // eslint-disable-next-line no-console const log = verbose ? (...args) => console.warn(...args) : noop const cacheOptions = {files, variablePattern, cwd} const {refs} = getCachedVariables(cacheOptions, log) return (root, result) => { root.walkDecls(decl => { for (const [name] of matchAll(decl.prop, variablePattern)) { if (!refs.has(name)) { stylelint.utils.report({ message: messages.rejected(name), node: decl, result, ruleName }) } else { const path = stripCwd(decl.source.input.file) log(`${name} declared in ${path} ref'd in ${pluralize(refs.get(name).size, 'file')}`) } } }) } }) function getCachedVariables(options, log) { const key = JSON.stringify(options) return cache.tap(key, () => { const {files, variablePattern} = options const decs = new TapMap() const refs = new TapMap() log(`Looking for variables in ${files} ...`) for (const file of globby.sync(files)) { const css = readFileSync(file, 'utf8') for (const match of matchAll(css, variablePattern)) { const after = css.substr(match.index + match[0].length) const name = match[0] if (after.startsWith(COLON)) { decs.tap(name, set).add(file) } else { refs.tap(name, set).add(file) } } } log(`Found ${decs.size} declarations, ${pluralize(refs.size, 'reference')}.`) for (const [name, files] of decs.entries()) { const fileRefs = refs.get(name) if (fileRefs) { log(`variable "${name}" declared in ${pluralize(files.size, 'file')}, ref'd in ${fileRefs.size}`) } else { log(`[!] variable "${name}" declared in ${Array.from(files)[0]} is not referenced`) } } return {decs, refs} }) } function noop() {} function set() { return new Set() } function stripCwd(path) { return path.startsWith(cwd) ? path.substr(cwd.length + 1) : path } function pluralize(num, str, plural = `${str}s`) { return num === 1 ? `${num} ${str}` : `${num} ${plural}` } |