diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e876d11..cb09249b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 9.2.1 + +### :bug: Bug fixes + +- Fix slow runtime by caching variable definitions in `primer/no-undefined-vars` rule +- Fix duplicate errors in `primer/no-undefined-vars` rule + # 9.2.0 ### :rocket: Enhancements diff --git a/package-lock.json b/package-lock.json index 1d1df8fc..f42ea2cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "stylelint-config-primer", - "version": "9.2.0", + "version": "9.2.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c57fefa1..6bead899 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stylelint-config-primer", - "version": "9.2.0", + "version": "9.2.1", "description": "Sharable stylelint config used by GitHub's CSS", "homepage": "http://primer.style/css/tools/linting", "author": "GitHub, Inc.", diff --git a/plugins/no-undefined-vars.js b/plugins/no-undefined-vars.js index e3d276e3..b63179ad 100644 --- a/plugins/no-undefined-vars.js +++ b/plugins/no-undefined-vars.js @@ -2,6 +2,7 @@ const fs = require('fs') const stylelint = require('stylelint') const matchAll = require('string.prototype.matchall') const globby = require('globby') +const TapMap = require('tap-map') const ruleName = 'primer/no-undefined-vars' const messages = stylelint.utils.ruleMessages(ruleName, { @@ -25,9 +26,18 @@ module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}) => { const log = verbose ? (...args) => console.warn(...args) : noop const definedVariables = getDefinedVariables(files, log) + // Keep track of declarations we've already seen + const seen = new WeakMap() + return (root, result) => { root.walkRules(rule => { rule.walkDecls(decl => { + if (seen.has(decl)) { + return + } else { + seen.set(decl, true) + } + for (const [, variableName] of matchAll(decl.value, variableReferenceRegex)) { if (!definedVariables.has(variableName)) { stylelint.utils.report({ @@ -43,18 +53,24 @@ module.exports = stylelint.createPlugin(ruleName, (enabled, options = {}) => { } }) +const cwd = process.cwd() +const cache = new TapMap() + function getDefinedVariables(files, log) { - const definedVariables = new Set() + const cacheKey = JSON.stringify({files, cwd}) + return cache.tap(cacheKey, () => { + const definedVariables = new Set() - for (const file of globby.sync(files)) { - const css = fs.readFileSync(file, 'utf-8') - for (const [, variableName] of matchAll(css, variableDefinitionRegex)) { - log(`${variableName} defined in ${file}`) - definedVariables.add(variableName) + for (const file of globby.sync(files)) { + const css = fs.readFileSync(file, 'utf-8') + for (const [, variableName] of matchAll(css, variableDefinitionRegex)) { + log(`${variableName} defined in ${file}`) + definedVariables.add(variableName) + } } - } - return definedVariables + return definedVariables + }) } function noop() {}