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 15x 15x 24x 24x 24x 9x 15x 3x 3x 9x 9x 3x 6x 3x 15x 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}`
}
|