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 16x 1x 15x 15x 15x 15x 15x 15x 20x 4x 2x 2x 2x 15x 15x 3x 3x 3x 3x 3x 12x 12x 21x 21x 21x 6x 15x 3x 3x 6x 6x 3x 3x 3x 12x 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}`
}
|