-
-
Notifications
You must be signed in to change notification settings - Fork 101
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
Make it so relative proxyquired paths are relative to the module being required. #190
Conversation
lib/proxyquire.js
Outdated
// be resolved. | ||
Proxyquire.prototype._resolveRelativePath = function(path, moduleFilename) { | ||
try { | ||
if(path[0] === '.') { |
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.
Perhaps I'm overthinking this, and we should just resolve everything, instead of only the relative paths. Then we wouldn't have to resolve them all a second time in the cleanup part of _withoutCache()... Yeah, I think I'm going to make that change.
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.
Except, when I try this it breaks a bunch of unit tests. Maybe I'll leave it alone. :)
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.
if (
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.
Until there is only a single place for resolving paths, I don't think I'm ready to merge this.
There's also no need to prepend resolved
to stubs. It may seem clear but it just removes clarity from the functions that have both stubs
and resolvedStubs
in scope.
package.json
Outdated
@@ -4,7 +4,8 @@ | |||
"description": "Proxies nodejs require in order to allow overriding dependencies during testing.", | |||
"main": "index.js", | |||
"scripts": { | |||
"test": "mocha" | |||
"test": "mocha", | |||
"lint": "jshint index.js lib" |
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.
Hey please don't do this, contributing a linter in an unrelated PR is quite unwelcome
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.
Oh, I noticed there was a .jshintrc
in the root folder. :P I'll pull it out.
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.
Yeah yeah my fault I didn't even realize there was jshintrc stuff in there. Since it hasn't been in CI for a while I'm just going to replace it with something a bit more ambitious/opinionated that's actually catching bugs.
lib/proxyquire.js
Outdated
// be resolved. | ||
Proxyquire.prototype._resolveRelativePath = function(path, moduleFilename) { | ||
try { | ||
if(path[0] === '.') { |
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.
if (
lib/proxyquire.js
Outdated
var resolvedPath = Module._resolveFilename(path, module); | ||
var self = this; | ||
|
||
var resolvedStubs = {}; |
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.
Try not to assign into objects as a side effect like this. stubs = Object.keys(stubs).reduce(reducer, {})
.
lib/proxyquire.js
Outdated
Proxyquire.prototype._withoutCache = function(module, stubs, path, func) { | ||
// Temporarily disable the cache - either per-module or globally if we have global stubs | ||
var restoreCache = this._disableCache(module, path); | ||
var resolvedPath = Module._resolveFilename(path, module); | ||
var self = this; |
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.
Don't use self = this
except when necessary. [].reduce
and its derivatives (e.g. map
) take a thisArg
as their last argument.
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.
Really? The MDN page doesn't mention this. In ES6 I would just use an arrow function. I suppose I could just use 'bind'.
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.
Hmm I guess it's only reduce that's missing it. It has an extra arg (the initial value) but I always consider it the "base" list function since it's the only one you can use to make all the others without loops or closures. Feel free to use self = this
for [].reduce
or bind
. No perf concern here so either is ok.
I wouldn't mind adopting some light ES6 syntax (node 4 compatible) sometime in the future but let's not change that now.
Oh I see there's some jshint stuff sprinkled around. It's not maintained. I'll land a linting change separately, probably standard. |
Just pushed a big update that uses standard, my preferred tool. The conflicts shouldn't be too bad (just toss package.json). Hopefully this helps make style consistency less ambiguous/easier. I've also been kicking around the idea of spending some time each day touching up the tests as a way to re-familiarize myself with the project. Keen on trying to resolve bugs like this and caching issues. I've never personally run into them which is probably a reflection of how I structure projects. Some folks at my new job are starting to adopt pq so I should have some opportunities to focus on it more regularly. |
There we go - I think that grabs all your changes, and it's rebased on master so it's all "standard"ized. |
Nice! I should have time to have a look at this again today. Particularly keen to see what's failing with respect to non-relative packages. |
The problem is that the unit tests try to Now that we resolve them ahead of time, though, so we hit the error earlier, and have to decide what to do. This fix just leaves them unchanged. It also means we'll try to clean them up from the cache now. |
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.
Looking good, appreciate all your efforts here! Made a few comments, will take another pass pre-merge and verify that the test fails without the source changes.
lib/proxyquire.js
Outdated
// "relative" paths (e.g. "./foo") but not non-relative modules (e.g. "react"). | ||
// Also, if noPreserveCache(), then we won't throw if the module can't | ||
// be resolved. | ||
Proxyquire.prototype._resolveRelativePath = function (path, moduleFilename) { |
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.
_resolvePath
, it may only be doing stuff with relative paths but it's responsible for parsing all stub paths.
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.
Oh, good catch. :)
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.
The comment here is now wildly incorrect, too. :)
lib/proxyquire.js
Outdated
@@ -4,6 +4,7 @@ | |||
var Module = require('module') | |||
var resolve = require('resolve') | |||
var dirname = require('path').dirname | |||
var pathResolve = require('path').resolve |
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.
Much as it'll be a bit disruptive I think we should just do var path = require('path')
and rename other "path" names to be more descriptive. I'd rather someone see path.*
especially for fns that aren't obviously named (resolve).
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.
Yeah, I was thinking the same thing. I'll do this.
lib/proxyquire.js
Outdated
if (hasOwnProperty.call(stubs, path)) { | ||
var stub = stubs[path] | ||
var resolvedPath = this._resolveRelativePath(path, module.filename) | ||
|
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.
no linebreak since this gets used into the if block immediately after
lib/proxyquire.js
Outdated
|
||
if (stub === null) { | ||
// Mimic the module-not-found exception thrown by node.js. | ||
throw moduleNotFoundError(path) | ||
throw moduleNotFoundError(resolvedPath) |
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.
Shouldn't this be the original user input? This is simulating the error message node would generate and that would be based on the require
id the user passes, not the resolved 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.
Yes. :)
lib/proxyquire.js
Outdated
}) | ||
var ids = [id].concat(stubIds.filter(Boolean)) | ||
|
||
var stubIds = Object.keys(stubs) |
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.
You can merge this line with the next now (Object.keys(stubs).filter(Boolean)
)
lib/proxyquire.js
Outdated
} catch (err) { | ||
// If this is not a relative path (e.g. "foo" as opposed to "./foo"), and | ||
// we couldn't resolve it, then we just let the path through unchanged. | ||
if (path[0] !== '.') { |
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.
Thoughts on using https://github.com/mattdesl/relative-require-regex here?
IMO an absolute path that couldn't be resolved should also pass through. External module also has the benefits of separate tests and docs.
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.
Could do - it's not a very exciting module: https://github.com/mattdesl/relative-require-regex/blob/master/index.js :P I tend to lean towards not using dependencies if I can avoid them.
The only thing different between their version and mine is, they would count something that starts with a "/" as a relative path. We're only going to hit this case when the path doesn't exist, so it's already a super rare case. Really this is here to keep behavior consistent with what used to happen (and to make the unit tests pass, since they bring in global modules that don't exist).
If you have strong feelings about it one way or the other, though, I'm happy to go either way.
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.
To me it comes down to whether you exactly reproduce node or mostly reproduce it in the ways we think matter. I'd rather err on the side of trying to replicate node behavior directly and that errs on the side of external packages since there are lots of cases to manage. I agree that this is exceptionally unlikely to matter but I'd rather build to spec.
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.
We aren't really trying to reproduce node behavior here, though. Any path that starts with a "." or ".." is unsafe for us to let through, because it wasn't resolved, but it could resolve relative to some other source file (that's exactly the issue I raised in the first place). A file that starts with a "/", on the other hand, can only match one file on the disk, so it's safe to let through even if the file doesn't exist. We just want to make sure that anything we return from this function is a "unique" module name.
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.
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.
Cool. Keep it is as for now. I'll look to remove that path eventually.
Thank you again for your help here! I verified that the test fails in the absence of your changes ( |
Thanks for merging! I would put a note somewhere. Maybe add a changelog.md?
We had tests in our codebase that fail with this change, because someone
was trying to proxyquire a file that wasn't being required but the file
directly, but by some other file.
…On Mar 2, 2018 17:23, "Ben Drucker" ***@***.***> wrote:
Thank you again for your help here! I verified that the test fails in the
absence of your changes (git checkout master -- lib/*). Waiting for
Travis now just to be safe and then I'll push a new major. Since this is a
bug and the major bump is just to be conservative/practical I don't think
there's a need to mention the change in the readme.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#190 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABsF-0FlZ8cjb8r_PPYKjPvqSdw-CspUks5tacZOgaJpZM4SXvqi>
.
|
It would be great if you could put a note into the v2.0.0 release: https://github.com/thlorenz/proxyquire/releases/tag/v2.0.0 or create a changelog Lots of folks will look for that :) if you like to automate releases (incl. the change logs) check out https://github.com/semantic-release/semantic-release ;) |
Updated the release with a short note. Not a fan of changelogs for small projects with low volume since it creates an expectation of updates on every release versus asking users to actually look at the changes. Putting some bullets on GH releases that are noteworthy is fine though. |
Previous code was based on an error in proxyquire: <2.0.0 thlorenz/proxyquire#190
Fixes #189.
I added a test case that fails without this fix, and passes with it.
This should definitely be a major version rev. It's possible someone is currently doing something like:
Which would no longer work with this change. I can do a writeup for the README.md that explains this breaking change.