eslint: Add 'native' as well as 'windows' and 'unix' to linebreak-style

This is a duplicate of request #8596, which already has a closed pull request #8620 which was designed to fix this. I realise this will therefore most likely be closed summarily, but I did just want to try to put the reasons for this change fowards again. I suspect that this may have been rejected by people who don’t have extensive experience of working on a projects where some developers are on Linux and some on Windows - because the approaches suggested as alternatives to allowing this fix really, truly, don’t work well.

I’ll try to be as brief as reasonably possible:

  • If you don’t specify anything on git then it is easy to end up with a mixed Linux-Windows project which has mixed line endings in its files which (we all agree) is horrible
  • For Linux-Windows projects on git, the standard approach to resolving this is to either set the git core.autocrlf config variable to true at the repository (or global) level or, even better (because it forces the equivalent of this setting on everyone who clones the repo), to have a .gitattributes file in the repo root containing the line * text=auto
  • As we know, this results in all text files in the repo itself having LF line endings, but all text files checked out to Windows machines (only) appearing on Windows to have CRLF line endings, and with such files (and changes to them) checked back in again ‘magically’ as LF
  • Mainly, this just works. We can also safely say there’s a reason the git developers did this, and that it’s the standard approach to this problem. Which is, mainly, that it just works.
  • The advantages of it are:
    • All programs on the OS (even older ones, such as Windows notepad, for example) are presented with native line endings, which they can read just fine
    • Any new files, created by anything, are essentially by definition correct, produced with native line endings and (correctly) added to the repository with LF line endings (and no warnings)
    • Any files which get modified by build processes (sometimes with changes which might need to be checked in, but sometimes with no changes) are by definition correct
      • What this lets you avoid is an LF file which has been ‘modified’ by some Windows process to have exactly the same contents but now with CRLF line endings
      • I am here because npm run build in a create-react-app project on Windows DOES exactly this - it rewrites tsconfig.json, typically with no actual changes, but with Windows CRLF line endings regardless of what it started with (yes, there is a bug here in what this process does; but such bugs are widespread and almost unavoidable in practice, in a complex build set-up)

Yes, we definitely want to avoid mixed line endings in files. But I am convinced (along with the original proposer) that the most correct way to do this is to allow the standard, clean, safe core.autocrlf true (aka * text auto) behaviour by git and to have an eslint linebreak-style native variant of the rule which (automatically, nothing else needed) matches that standard, safe approach.

NB, also, using the git core.autocrlf approach but turning off the eslint rule (which seems to be the main suggestion, apart from forcing LF endings onto Windows machines, which is problematic for the types of reasons given) works, of course… as long as everything was set up right in the first-place, but it removes all verification - which eslint could otherwise provide! and surely that is part of its purpose?! - that everything was indeed set up right.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 3
  • Comments: 25 (6 by maintainers)

Commits related to this issue

Most upvoted comments

This was the revised version, I’ve tried to keep the tone very neutral, and not-overladen with warnings, just to simply mention the issue:


Options

This rule has a string option:

  • "unix" (default) enforces the usage of Unix line endings: \n for LF.
  • "windows" enforces the usage of Windows line endings: \r\n for CRLF.
  • "native" enforces OS-native linefeeds (\r\n for CRLF on Windows, \n for LF on Unix).

unix

Examples of incorrect code for this rule with the default "unix" option:

<no change>

windows

Examples of incorrect code for this rule with the "windows" option:

<no change>

native

Examples of incorrect code for this rule with the "native" option:

/*eslint linebreak-style: ["error", "native"]*/

var a = 'a', // \n     // fails on Windows
    b = 'b'; // \r\n   // fails on Unix

Examples of correct code for this rule with the “native” option:

/*eslint linebreak-style: ["error", "native"]*/

// passes on Windows
var a = 'a', // \r\n
    b = 'b'; // \r\n
/*eslint linebreak-style: ["error", "native"]*/

// passes on Unix
var a = 'a', // \n
    b = 'b'; // \n

Using this rule with version control systems

Version control systems sometimes have special behavior for linebreaks. For example, the default behavior of git on Windows systems is to convert LF linebreaks to CRLF when checking out files, but to store the linebreaks as LF when committing a change.

To make it easy for developers to contribute to your codebase from different platforms, you should configure your VCS to handle linebreaks appropriately.

If you use git, one option is to add a line to your .gitattributes file to prevent git from converting linebreaks in .js files:

*.js text eol=lf

That works with the default "unix" option for the linebreak-style rule.

Another option is to explicitly ask git to use native linebreaks for all users, regardless of their personal settings, by adding the following line to your .gitattributes file:

* text=auto

This will work with the "native" option for the linebreak-style rule. When doing this, exercise caution since the build output of your project will inherit the local linebreak style of the OS on which the build was made. Many developers consider this to be fine for development work - we leave that choice up to you - but it is not fine for release builds, and may even break the release for users on the wrong OS. If you are using a modern continuous integration approach this problem will not arise so long as you configure things so that your release builds are always made automatically, on a known image of a known OS.

@MikeBeaton Sorry about the miscommunication. In this repo it’s common for people to use 👎 to simply express opposition to adding a feature, without the other connotations you suggested. I’m undecided on whether this feature would be worthwhile, but I don’t think anyone would read this and think you’re wasting peoples’ time. (Hopefully @mysticatea will be able to elaborate on his reasoning for opposing this feature request soon.)

So, I actually have a create-react-app project which I’m working on now. It’s being created for our company by an external company whose developers use VS Code on macOS, but our developers are going to be working on it too, using VS Code on Windows.

When I first cloned the project onto my Windows machine and built it, I got lots of errors as follows:

screenshot_1

The project is using the default eslint 'linebreak-style': 'unix', but because I have my Windows git set up in a fairly standard way, and because we hadn’t yet specified anything else, all the text files on Windows are checking out as CRLF. I obviously can’t change the linebreak-style to windows, that would break the Mac build. (Nor, of course, should I run eslint --fix. That would be a bad choice in this state, although there is an error message recommending it, and a more junior programmer might be tempted to try that as a fix!)

I can just turn off the warnings by setting 'linebreak-style': 'off' in .eslintrc.js and this works - of course. But I don’t want to just turn off the warnings, that’s the whole point of lint, right? (And it is certainly possible, in a mixed Windows Un*x project, that some file will turn up somewhere with mixed line endings, from an erroneous commit by someone. They should be told, and I’d like to know.)

So the only real option (apart from just telling eslint ‘hey, don’t worry about it’) is to switch to a .gitattributes file such as the following:

# check out all text files as LF always on all OSes
* text eol=lf

# make sure that these files aren't modified
*.png binary
*.ico binary
*.jpg binary
*.jpeg binary

Now we’re getting somewhere. Although note that once you’ve specified * text eol=lf things already start getting harder, and you now have to start explicitly specifying all your binary files - which you don’t have to with * text auto, where git automatically does what it thinks is right - which it mostly is. We need to delete everything and clone it again, but now the software will compile on Windows without triggering the eslint warning - because the line endings are now LF, even on Windows!

But we have disadvantages to this.

Here’s one:

screenshot_2

Something in the toolchain has inserted some CRLFs into a file which shouldn’t have them - and which is otherwise unchanged; as verified by the fact that after running dos2unix on the file (as shown), the spurious file change message goes away.

Yes, this is a bug in something in the react toolchain, but that doesn’t really matter (I think). It is the near impossibility of fully avoiding errors like this which is the reason why git provides the core.autocrlf setting in the first place.

And note that we have other disadvantages.

I can no longer read my files in ‘quick and dirty’ tools like Windows notepad, even if I want to:

screenshot_3

I have to be very careful creating new files:

screenshot_4

Many Windows programs will (of course) create new files with CRLF in them. So I get a warning when I try to add them to a commit. It will in fact work, but I have to get in the habit of ignoring lots of warnings (bad), and I will be in a situation where my local files (the ones I added, with CRLF) will no longer be the same as they would be if I deleted and re-cloned the repo (when they would become LF). Otherwise I have to be much more careful, and remember to dos2unix all text files which I add (and when - not if - I forget to do this, I have to back out the adds, convert them, and then re-add them). This is a world of pain…

Now, as far as I can see, if I could have set 'linebreak-style': 'native' in .eslintrc.js in the first place (i.e. in combination with the * text auto in .gitattributes) then none of this would have happened, and everything would be peachy! Windows programs could insert Windows linebreaks in files on Windows without generating spurious change messages. New files could be freely created (with their default Windows linebreaks) and correctly committed with Unix linebreaks (to the repo, and to the Mac developers). And eslint is checking that all this is all alright.

For example, if we had forgotten to put anything in .gitattributes in the first place, and if some Windows developer had their global core.autocrlf set to false, then they could easily have ended up committing CRLF files back into an LF repo. Bad. But immediately noticed. By eslint running in the CI and on the Mac developers’ machines, and complaining that it was seeing non-native line endings.

There’s one disadvantage to this, which is that there are now two substantively different builds hanging around - a build where everything is in CRLF, built on Windows, and a build where everything is in LF, built on macOS (or, more generally, on some Un*x system). But that doesn’t matter, right? The only builds I will ever build on my local machine are development builds, and they work just fine with either line ending. All test, staging and production builds are done on our CI server and… it’s our responsibility to set up our CI correctly! But basically, it’s going to be (or, in actual fact, already is) running on a Un*x docker image, and it’s going to build the expected version, with LFs. And (because eslint is now playing along - in this imagined new world!) it’s going to be checking, while it builds, that no mixed or Windows line endings have been introduced.