-
-
Notifications
You must be signed in to change notification settings - Fork 105
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
Normalize paths #101
Normalize paths #101
Conversation
return this._cwd; | ||
}, | ||
set: function(cwd) { | ||
if (typeof cwd !== 'string') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This currently allows an empty string for cwd
; do we want to allow that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@phated dont think so
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, will add a check for it.
Related: do you think we should expand the custom |
@phated the For example: // on windows
path.normalize('/test/foo'); // => \\test\\foo The only kinda (since none codes like this) issue is the other way around: // on linux
path.normalize('\\test\\foo'); // => \\test\\foo Unfortunately nothing can be done about it, since In it's current form, Vinyl can be used either as a POSIX compliant module if user is only targeting UNIX systems and needs to use '' in filenames, or a cross platform module by choosing not to use But, on the other hand, if you are saving normalized paths to some database on windows, and than you move said database to linux, you are screwed :) If you ask me, I'd love to just hard convert everything to And just to note: the custom normalize function is there because |
@@ -135,7 +141,7 @@ File.prototype.inspect = function() { | |||
var inspect = []; | |||
|
|||
// Use relative path if possible | |||
var filePath = (this.base && this.path) ? this.relative : this.path; | |||
var filePath = this.path ? this.relative : this.path; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this.path
at the end of the ternary? Wouldn't it be something falsey always? Could probably get away with hardcoding an empty string or using undefined/null instead. 1 less getter call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad. I've just removed the now unnecessary this.base
check without re-evaluating the ternary. That's how you arrive at ternaries like true ? foo : bar
:) Gonna change it to null
.
@darsain I'm fine with leaving the normalization up to node's I had a few follow-ups inline, but otherwise this is looking pretty good. |
@phated yup, gonna make the changes. What about your question whether to disallow empty paths? You fine with this reasoning against it? |
- `file.stat` is an object | ||
- `file.stat.isDirectory()` returns `true` | ||
|
||
When marking file as a directory, do it by mocking the `fs.Stats` object, and passing it via `options.stat` property into the constructor. Vinyl needs to know the file is a directory from the get go, or it might result in an inconsistent path history (some entries ending in separator, some not). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like "When marking file as a directory". Maybe something like "When constructing a Vinyl object, pass in a valid fs.Stats
object. Vinyl needs to know the file is a directory from the get go, or it might result in an inconsistent path history (some entries ending in separator, some not). If you are mocking the fs.Stats
object, ensure it has the isDirectory
method.", or something similar.
@darsain after running the vinyl-fs tests with your changes linked in, I'm encountering some issues:
Should I get the symbolic changes merged and then you can rebase and tweak your implementation? |
Yeah that would be best. And can you clarify the simlink issue and solution? Will there have to be a getter that drops |
@darsain I believe the setters/getters that need to take |
@darsain you can link vinyl into vinyl-fs master and run the tests. The failing tests related to this are in |
I've merged my |
Hm... currently, the stem doesn't have a separator appended when directory. Should it? |
79f4caf
to
42fa033
Compare
Rebased, and these props now don't end in separator when both
Also added tests for this. There is still the question of what to do with |
My quick thought is that stem should end in a trailing separator if it's a directory. Any reason it shouldn't? |
Well now that I think about it, what if you want to do something that was previously super simple, like append to the basename/stem? If all will end in separator, you have to first run it through something that strips slashes. We made concatenation easier, but renaming harder. It's especially evident with basename, where if you want to append to it and it's a directory, you have to run basename through path.basename to get what you need: file.basename = path.basename(file.basename) + '-appendinx'; // path.basename strips slashes It feels silly, redundant, and will also break all gulp renaming plugins, and all code currently dependent on simple appending to basename/stem. What is currently simple will need a uniform slash stripping, or a special case for In all coding environments, renaming/prepending/appending is straight forward, while you use something special to concatenate path levels (in node's case path.join), and we just made it the opposite. I think that all we'll accomplish by automatic trailing separator for directories is just switch Lets compare pros/cons of trailing separator for directories. Pros:
Cons:
All renaming and retrieving slashless path and parts of path will be especially cumbersome, since strip trailing slashes function is not part of any core lib, so you have to substitute with something like And it's not like we got rid of an unnecessary complexity here. path.join definitely adds a complexity, but it seems like it's the necessary kind. That's why its equivalents are part of every stdlib out there with no commonly used alternatives that would do it better. Also, we didn't even got rid of it completely. You don't need it only in those few concatenation examples above. You'll still need it in tons of other common operations, such as adding a directory level: file.path = path.join(file.dirname, 'new-level', file.basename); So you'll still need to path.join often, but now on top of that you have to strip slashes when:
The trailing separator for directories didn't feel quite right for me from the beginning, but while implementing it I just keep on stumbling into reasons why not to do it. Besides, with automatic normalization, you can just concatenate with file.path = file.dirname + '/' + file.basename; There is also a value in having everything not end in a slash, since some tools/operations will complain when it does. We've already run into it even before merging this PR when vinyl-fs simlinking tests started failing. So, are those few concatenation examples in the Pros list worth all the hassle and breakage that trailing separator is gonna introduce? Overall, I now think that consistently never ending in a separator is way more practical and robust than doing the opposite. |
@darsain I think your observations are correct and forcing the separator isn't the correct way to normalize paths. This problem I was trying to solve with this suggestion is at https://github.com/gulpjs/vinyl-fs/blob/master/lib/prepare-write.js#L47-L49 Is there a way we can solve that problem with normalization (such as always removing the trailing slash, etc)? I am open to altering the vinyl-fs tests but everything should be consistent. An aside: do you think we should have |
@darsain I thought node's |
@phated nope. It just normalizes the path :) resolves dots and switches separators to the separator of the current platform. Stripping trailing separators needs to be done separately. |
@@ -124,8 +135,14 @@ if (file.isBuffer()) { | |||
} | |||
``` | |||
|
|||
### cwd | |||
Ges and sets current working directory. Defaults to `process.cwd`. Will always be normalized and stripped off a trailing separator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Gets" typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"stripped off" -> "remove"
Looks like my only problems are docs and test nits. I do want to further discuss the history normalization when no |
Looks like vinyl-fs tests are passing except for tests that had trailing separators. I'll merge and do a cleanup pass on my nits. |
@darsain Thanks for all the work on this! Really glad you found the issues with my trailing separator thinking. This will be released in the next major, after I land a few more changes. |
Windows tests passing for the first time ever?!?! https://ci.appveyor.com/project/gulpjs/vinyl |
Changes:
Normalization: passing a path through
path.normalize()
and stripping any trailing separators.file.cwd
andfile.base
are now a get/set objects that normalize the path on set.file.history
when passed.file.path
normalizes on set.file.base
now proxies tofile.cwd
whennull
,undefined
, or equal tofile.cwd
. Read more infile.base
changes below.Test changes:
file.base
changes below for more):file.base
changesIt was already being set to
file.cwd
in constructor so it was never falsy or undefined unless manually made so. This made it tedious when you wanted to changefile.cwd
as you had to keep the two in sync.This PR makes
file.base
a proxy tofile.cwd
unless set to something else. It can be reset back to "proxy tofile.cwd
" behavior by passingnull
,undefined
, or value same asfile.cwd
.This change made the use of both
file.cwd
andfile.base
props easier, simplifiedfile.inspect()
andfile.relative()
, and removed thefile.relative()
error case when there was nofile.base
set.