-
-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] Poor performance of no-custom-classname
and classnames-order
#276
Comments
We're hitting the same issue (next.js project with a single css file: |
Added the following to my eslint-config package as a stop-gap measure, but the underlying issue is incredibility unacceptable, given these rules often account for over 50s. If you don't have time to resolve this, could you perhaps point me in the right direction? // ...
export = declare({
overrides: [
{
files: ["**/*.{js,jsx,ts,tsx}"],
plugins: ["tailwindcss"],
extends: ["plugin:tailwindcss/recommended"],
settings: {
/**
* Performance issue with the plugin, somewhat mitigated setting cssFiles to an empty array.
* @see https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/276
* @see https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/174
*/
tailwindcss: {
cssFiles: [],
},
},
rules: {
"tailwindcss/no-custom-classname": ["warn", options],
"tailwindcss/no-contradicting-classname": ["warn", options],
"tailwindcss/classnames-order": ["warn", options],
},
},
],
}); |
It looks like a lot of time is spend in this function
Perhaps a quick improvement would be to move the |
+1 on this. Also experiencing extreme slowdown of ESLint within VSCode, especially when format on save. |
+1 Here too
|
+1 here
|
any way of skipping this rule?
|
@rickvandermey you can add this to your {
"rules": {
"tailwindcss/no-custom-classname": "off",
}
} Even though the rule is slow, I personally find it very useful and don’t recommend disabling it. |
ended up by removing |
In my case diff --git a/.eslintrc b/.eslintrc
index ceda0b46..16039373 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -4,7 +4,8 @@
"ignorePatterns": ["**/node_modules"],
"settings": {
"tailwindcss": {
- "config": "./workspace/packages/config/src/tailwind.config.js"
+ "config": "./workspace/packages/config/src/tailwind.config.js",
+ "cssFiles": []
}
},
"extends": [ Before:
After:
I think cssFiles detection logic is ineffective, we should investigate it |
There are 3 problems.
eslint-plugin-tailwindcss/lib/util/cssFiles.js Lines 18 to 41 in 7070c34
I can provide PR that fixes this behavior |
For me
|
The @francoismassart can you suggest an approach to mitigate this? Or is there any plan to do something about it? Thanks! |
Main problem of this rule - it tries make sync scan of file system .css files to find class names. We can improve this speed of this rule in couple of times, but it will not be able to be fast until |
After my fix there are still a problem: plugin will traverses file system every 5s. Basically we should not do it at all for cases when we are not inside of LSP. Probably it will add this kind of flag for further improvements |
Checked in our react-native project Before:
After:
You can improve speed of plugin with this patch: diff --git a/lib/util/cssFiles.js b/lib/util/cssFiles.js
index 4a49a33..900aaff 100644
--- a/lib/util/cssFiles.js
+++ b/lib/util/cssFiles.js
@@ -3,12 +3,17 @@
const fg = require('fast-glob');
const fs = require('fs');
const postcss = require('postcss');
-const removeDuplicatesFromArray = require('./removeDuplicatesFromArray');
-let previousGlobsResults = [];
+const classRegexp = /\.([^\.\,\s\n\:\(\)\[\]\'~\+\>\*\\]*)/gim;
+
let lastUpdate = null;
let classnamesFromFiles = [];
+/**
+ * @type {Map<string, number}
+ */
+const prevEditedTimestamp = new Map()
+
/**
* Read CSS files and extract classnames
* @param {Array} patterns Glob patterns to locate files
@@ -17,27 +22,56 @@ let classnamesFromFiles = [];
*/
const generateClassnamesListSync = (patterns, refreshRate = 5_000) => {
const now = new Date().getTime();
- const files = fg.sync(patterns, { suppressErrors: true });
- const newGlobs = previousGlobsResults.flat().join(',') != files.flat().join(',');
const expired = lastUpdate === null || now - lastUpdate > refreshRate;
- if (newGlobs || expired) {
- previousGlobsResults = files;
- lastUpdate = now;
- let detectedClassnames = [];
- for (const file of files) {
- const data = fs.readFileSync(file, 'utf-8');
- const root = postcss.parse(data);
- root.walkRules((rule) => {
- const regexp = /\.([^\.\,\s\n\:\(\)\[\]\'~\+\>\*\\]*)/gim;
- const matches = [...rule.selector.matchAll(regexp)];
- const classnames = matches.map((arr) => arr[1]);
- detectedClassnames.push(...classnames);
- });
- detectedClassnames = removeDuplicatesFromArray(detectedClassnames);
+
+ if (!expired) {
+ return classnamesFromFiles;
+ }
+ const files = fg.sync(patterns, { suppressErrors: true, stats: true });
+ lastUpdate = now;
+
+ /**
+ * @type {Set<string}
+ */
+ const detectedClassnames = new Set();
+ /**
+ * @type {Set<string>}
+ */
+ const filesSet = new Set();
+ for (const { path: file, stats } of files) {
+ filesSet.add(file);
+ if (!stats) {}
+ // file is not changed -> we do need to do extra work
+ else if (prevEditedTimestamp.get(file) === stats.mtimeMs) {
+ continue;
+ } else {
+ prevEditedTimestamp.set(file, stats.mtimeMs);
}
- classnamesFromFiles = detectedClassnames;
+ const data = fs.readFileSync(file, 'utf-8');
+ const root = postcss.parse(data);
+ root.walkRules((rule) => {
+ for (const match of rule.selector.matchAll(classRegexp)) {
+ detectedClassnames.add(match[1]);
+ }
+ });
}
- return classnamesFromFiles;
+ // avoiding memory leak
+ {
+ /**
+ * @type {string[]}
+ */
+ const keysToDelete = []
+ for (const cachedFilePath of prevEditedTimestamp.keys()) {
+ if (!filesSet.has(cachedFilePath)) {
+ keysToDelete.push(cachedFilePath);
+ }
+ }
+ for (const key of keysToDelete) {
+ prevEditedTimestamp.delete(key);
+ }
+ }
+ classnamesFromFiles = [...detectedClassnames];
+ return classnamesFromFiles
};
module.exports = generateClassnamesListSync; |
|
Btw. Say how it performs in your cases? |
To be honest, I don't need much improvement in the case where |
Describe the bug
no-custom-classname
andclassnames-order
take a very long time to run relative to other rules.To Reproduce
Steps to reproduce the behavior:
Expected behavior
Execution time more in line with other rules. These two rules account for over 80% (>50s on a fast machine) of our eslint runtime.
Screenshots
Environment (please complete the following information):
Additional context
Setting cssFiles to
[]
per #174 makesno-custom-classname
almost 40% faster, but these rules still take around 40 seconds to run.eslint config file or live demo
The text was updated successfully, but these errors were encountered: