Skip to content
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

Error when lint staging files with $ in its name #962

Open
thefrana opened this issue Apr 6, 2021 · 12 comments
Open

Error when lint staging files with $ in its name #962

thefrana opened this issue Apr 6, 2021 · 12 comments

Comments

@thefrana
Copy link

thefrana commented Apr 6, 2021

Description

When I try eslint path/to/file/$something.foo it tries to resolve $something as an variable (I'm on Linux) and it becomes only path/to/file/.foo since variable does not exist. Is there any way how to resolve this issue? Specifically I'm using ESLint with lint-staged. I cannot properly escape the filename. Right now I am exporting variable with export something='$something', but that is far from ideal.

Do you have some workaround?

Steps to reproduce

Have some file with $ in its name and run lint-staged on it.

Environment

  • OS: Manjaro Gnome
  • Node.js: 14.16.0
  • lint-staged: 10.5.4
@iiroj
Copy link
Member

iiroj commented Apr 7, 2021

This PR might help you, but it's a bit stalled because I haven't the time and energy during COVID: #875.

Are you using the --shell option? AFAIK lint-staged doesn't by default use a shell, but directly calls the command with filenames as arguments, using execa.

@thefrana
Copy link
Author

thefrana commented Apr 7, 2021

No, I am not using shell option. Just lint-staged command. So maybe it's execa problem? I think the filenames should be escaped. I'll take a look at the PR. Thank you

@iiroj
Copy link
Member

iiroj commented Apr 9, 2021

Please post the debug logs, that way we can see what the actual filename passed to eslint was. You can setup lint-staged to print debug logs with lint-staged --debug.

@milamer
Copy link

milamer commented Aug 9, 2021

Hey there,
just had this issue and dug a bit.

I got it working by escaping the filenames.
I created two patches:
One for lint-stages/execa

diff --git a/node_modules/lint-staged/node_modules/execa/index.js b/node_modules/lint-staged/node_modules/execa/index.js
index 6fc9f12..891f53d 100644
--- a/node_modules/lint-staged/node_modules/execa/index.js
+++ b/node_modules/lint-staged/node_modules/execa/index.js
@@ -72,6 +72,9 @@ const handleOutput = (options, value, error) => {
 };
 
 const execa = (file, args, options) => {
+	if(options.escape) {
+		args = args.replace(/\$/g, '\\\$')
+	}
 	const parsed = handleArguments(file, args, options);
 	const command = joinCommand(file, args);
 	const escapedCommand = getEscapedCommand(file, args);

And one for lint-staged:

diff --git a/node_modules/lint-staged/lib/resolveTaskFn.js b/node_modules/lint-staged/lib/resolveTaskFn.js
index 8c67a69..8e4ec54 100644
--- a/node_modules/lint-staged/lib/resolveTaskFn.js
+++ b/node_modules/lint-staged/lib/resolveTaskFn.js
@@ -89,7 +89,7 @@ module.exports = function resolveTaskFn({
   debug('cmd:', cmd)
   debug('args:', args)
 
-  const execaOptions = { preferLocal: true, reject: false, shell }
+  const execaOptions = { preferLocal: true, reject: false, shell, escape: true }
   if (relative) {
     execaOptions.cwd = process.cwd()
   } else if (/^git(\.exe)?/i.test(cmd) && gitDir !== process.cwd()) {

@thefrana
Copy link
Author

thefrana commented Aug 9, 2021

Nice, thank you. Did you create PR for these patches? I think we are not the only ones with that issue.

@skriems
Copy link

skriems commented Aug 1, 2022

Hey 👋
Is there any plan to push this?

@mrexox
Copy link

mrexox commented Aug 19, 2022

Hey! I guess this is a NPM-related bug. Try running the executable without NPM wrapper. For me it worked (an example with eslint):

$ npx run eslint 'app/routes/service.$serviceId.tsx'
Oops! Something went wrong! :(

ESLint: 8.22.0

No files matching the pattern "app/routes/service..tsx" were found.
Please check for typing mistakes in the pattern.

$ node_modules/.bin/eslint 'app/routes/service.$serviceId.tsx'
$ echo $?
0

@iiroj
Copy link
Member

iiroj commented Aug 19, 2022

Doesn't seem to be a lint-staged issue, but rather npm?

@HuyAms
Copy link

HuyAms commented Sep 23, 2022

I faced up with the same issue

@allypalanzi
Copy link

same issue here

@Balastrong
Copy link

I was having the same issue as some of my paths have the $ sign.
For everyone else coming here in the future here the solution that worked for me (based on the comments above)

  1. Add --shell when using lint-staged

If you're using husky it will be this line in your package.json

- "precommit": "lint-staged"
+ "precommit": "lint-staged --shell"
  1. Create lint-staged.config.js in your root
function escapeDollar(filepath) {
	return filepath.replace(/\$/g, '\\$');
}

export default {
	'src/**/*.{js,jsx,ts,tsx}': (filenames) => [
		`prettier --write ${filenames.map(escapeDollar).join(' ')}`,
		`eslint --fix ${filenames.map(escapeDollar).join(' ')}`
	],
	'src/**/*.{css,json}': `prettier --write`
};

In my case I'm running both prettier and eslint, but I think you can adapt from this example to your specific case.

@iiroj
Copy link
Member

iiroj commented May 10, 2024

I added an e2e test for a difficult filename in #1413 and looks like it doesn't pass on Windows runners, but others are working fine by default. EDIT: Nevermind, this was actually failure in the test setup itself.

EDIT: So lint-staged should handle these files just fine at least for ESLint and Prettier:

  • $test.js
  • [test].js
  • (test).js

We are using execa under the hood and it promises that no manual escaping is needed. I don't recommend the --shell option because it disabled execa's default escaping and might introduce some security issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

8 participants