stylelint icon indicating copy to clipboard operation
stylelint copied to clipboard

Fix `ignoreFiles` being evaluated case-sensitively on Windows

Open adalinesimonian opened this issue 4 years ago • 9 comments

Tested on Stylelint 14

ignoreFiles is evaluated case-sensitively on Windows, leading to a few issues downstream in vscode-stylelint. I stumbled into this while migrating from the deprecated vscode-languageserver URI API to the new vscode-uri API. The new API generates lowercase drive letters for paths instead of uppercase letters as the old API did.

Let's say the project is in C:\Users\Adaline\Code\my-project. Suppose that there is a file named should-be-ignored.css and the Stylelint config is:

{ "ignoreFiles": "*-ignored.css" }

If you pass C:\Users\Adaline\Code\my-project\should-be-ignored.css to Stylelint, it ignores the file, as it should. But if you pass c:\...\should-be-ignored.css or any other case of any part of the path, Stylelint doesn't ignore it even though it can read the file correctly using the differently-cased path.

Node.js's API's behaviour, for reference, correctly sees these paths as equal on Windows:

const path = require('path');

path.win32.relative('C:\\Path\\to\\file.css', 'c:\\path\\To\\File.css')
// Evaluates to ''

I haven't checked any other config properties or functionality, so I'm unaware if there are other cases of this behaviour elsewhere in the project.

adalinesimonian avatar Oct 12 '21 23:10 adalinesimonian

Thanks for the report. Labelling as ready to implement.

jeddy3 avatar Oct 13 '21 09:10 jeddy3

stylelint is using process.cwd() , which can potentially return lower-case in environments. https://github.com/nodejs/node/issues/6624

I think we should avoid using process.cwd() direct values or indexing keys. It creates a lot of potential issues with caching. Here we can see it can store the same config twice with using upper-case drive path in codeFilename and when respectively, no upper-case drive config path is defined in options...

Case:

./.stylelintrc.json

{
  "processors": ["stylelint-processor-styled-components"],
  "extends": ["stylelint-config-standard-scss", "stylelint-config-styled-components"],
  "rules": {
    "selector-type-case": ["lower", { "ignoreTypes": ["/^\\$\\w+/"] }],
    "selector-type-no-unknown": [true, { "ignoreTypes": ["/-styled-mixin/", "/^\\$\\w+/"] }],
    "value-keyword-case": ["lower", { "ignoreKeywords": ["dummyValue"] }],
    "declaration-colon-newline-after": null
  },
  "overrides": [{
    "files": ["./**/*.js"],
    "customSyntax": "postcss-scss"
  }]
}

./foo.js

const foo = styled.div`
  .bar {
    line-height: 1em // CssSyntaxError missing semi-colon
  }
`;

./stylelint-runner-exception.js

import { logResult, logError } from './lintLogger';

await stylelint.lint({ codeFilename: 'C:\\Path\\to\\project\foo.js'  }).then(logResult).catch(logError);  
// exception from stylelint-processor-styled-component in logs e.g. Cannot access property "3" of undefined... instead of reporting missing semi-colon

// debugging details:
// calls getConfigForFile(stylelint._options.configFile); // searchPath defaulted to process.cwd()
// calls augmentConfigFull(config); // See https://github.com/stylelint/stylelint/blob/29acc54ef08174a6b0caa69d093b9e3bd3ba897f/lib/augmentConfig.js#L82
// - calls addProcessorFunctions, processorCache misses.
// - Require and add processor to cache from config

// linting begins with lintSource
// will fetch config again to check for ignore files. See line https://github.com/stylelint/stylelint/blob/a7d6e5651a7e43a7978c18798fa88e4fc2473c8f/lib/lintSource.js#L51
// calls getConfigForFile((stylelint._options.configFile || inputFilePath); // configFile is undefined
// calls augmentConfigFull(config);
// - calls addProcessorFunctions, processorCache misses.
// - Require and add processor to cache from config



console.log(Array.from(stylelint._specifiedConfigCache.keys())) // has both upper and lower case caches. Each config has their own caches, processors, plugins, etc! 
/*
* [
*  'c:\\Path\\to\\project\\.stylelintrc.json',
*  'C:\\Path\\to\\project\\.stylelintrc.json'
* ]
*/

console.log(stylelint._specifiedConfigCache.get('C:\\Path\\to\\project\.stylelintrc.json') === stylelint._specifiedConfigCache.get('c:\\Path\\to\\project\.stylelintrc.json')); 
// false

./stylelint-runner-no-exeception1.js

import { logResult, logError } from './lintLogger';

await stylelint.lint({ configFile: 'C:\\Path\\to\\project\.stylelintrc.json', codeFilename: 'C:\\Path\\to\\project\foo.js'  }).then(logResult).catch(logError);;
// logs the semi-colon error. Works!

console.log(Array.from(stylelint._specifiedConfigCache.keys()))
/*
* [
*  'C:\\Path\\to\\project\\.stylelintrc.json'
* ]
*/

./stylelint-runner-no-exception2.js

import { logResult, logError } from './lintLogger';

await stylelint.lint({ codeFilename: 'c:\\Path\\to\\project\foo.js'  }).then(logResult).catch(logError);
// also works

console.log(Array.from(stylelint._specifiedConfigCache.keys()))
/*
* [
*  'c:\\Path\\to\\project\\.stylelintrc.json'
* ]
*/

psychobolt avatar Feb 23 '22 05:02 psychobolt

This issue is older than one month. Please ask before opening a pull request, as it may no longer be relevant.

github-actions[bot] avatar Jan 22 '24 10:01 github-actions[bot]