-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
chore: don't remove optional dependencies in clean-shrinkwrap.js #1551
Conversation
Let's see what the CI servers say. Fingers-crossed! |
YESS, finally everything works \o/ |
7d79302
to
6132500
Compare
What do you think @jhermsmeier ? |
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 haven't parsed this fully yet, but looks good to me so far, just a few nits.
scripts/clean-shrinkwrap.js
Outdated
], getDependencyTree(shrinkwrapFile, dependencyPath))); | ||
}, []); | ||
|
||
const JSON_INDENTATION_SPACES = 2; |
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.
Can we declare this at the very top?
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.
Done
scripts/clean-shrinkwrap.js
Outdated
* @returns {Object} shrinkwrap object | ||
* | ||
* @example | ||
* const object = getShrinkwrapDependencyObject(require('npm-shrinkwrap.json'), [ |
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 required path needs a ./
to understand that it's a local path afaik – might be good to have that in the example as well?
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.
Fixed!
scripts/clean-shrinkwrap.js
Outdated
* @example | ||
* const dependencies = getTopLevelDependenciesFromShrinkwrapObject([ 'debug' ]); | ||
*/ | ||
const getTopLevelDependenciesFromShrinkwrapObject = (shrinkwrapPath) => { |
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 is starting to feel like I'm reading Java 😝
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.
Well, its JavaScript after all :P
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.
From what I can tell, would getTopLevelDependenciesForShrinkwrapPath
be a better name? *shrug*
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.
Done!
"platformSpecificDependencies": [ | ||
[ "7zip-bin-mac" ], | ||
[ "7zip-bin-win" ], | ||
[ "7zip-bin-linux" ] |
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.
What's the reasoning behind the nested arrays here?
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 new approach works based on shrinkwrap "paths", which we set using array syntax. Basically this means that we want to omit 7zip-bin-mac
from the top level of the shrinkwrap file for example. If 7zip-bin-mac
was present inside foo
in the shrinkwrap file, we should add [ 'foo', '7zip-bin-mac' ]
.
6132500
to
2a000ae
Compare
scripts/clean-shrinkwrap.js
Outdated
* @returns {Object} shrinkwrap object | ||
* | ||
* @example | ||
* const object = getPrettyShrinkwrapDependencyObject(require('npm-shrinkwrap.json'), [ |
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.
Another relative path missing in the example here
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.
Fixed!
There's also a build failure on Windows:
Is this a transient error, that only occasionally occurs? |
Yeah, it keeps popping up now and then, but only on Appveyor, and we've no idea why :-( When you restart the build (which I've just done), it usually passes on the second attempt. |
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.
With all the hardcore hacking @jviotti has been doing on npm-shrinkwrap.json
these past few months, I hope he doesn't get poached by the NPM project :-D
* const object = getShrinkwrapDependencyObject(require('./npm-shrinkwrap.json'), [ | ||
* 'drivelist', | ||
* 'lodash' | ||
* ]); |
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.
Should these examples also show what the returned object might contain?
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.
Sure, I'll add some more example code.
scripts/clean-shrinkwrap.js
Outdated
* @private | ||
* | ||
* @description | ||
* This function transforms the output of `getShrinkwrapDependencyObject()` to |
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.
minor nit: could you change transforms the output of
to wraps
? The current description makes it sounds like getPrettyShrinkwrapDependencyObject
takes the output of getShrinkwrapDependencyObject
as an input parameter, which it doesn't.
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.
Fixed!
scripts/clean-shrinkwrap.js
Outdated
* | ||
* @param {Object} shrinkwrap - the shrinkwrap file contents | ||
* @param {String[]} shrinkwrapPath - path to shrinkwrap dependency | ||
* @returns {Object} shrinkwrap object |
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 this should be @returns {Object} pretty shrinkwrap object
?
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.
Fixed
* @private | ||
* | ||
* @param {String[]} shrinkwrapPath - path to shrinkwrap dependency | ||
* @returns {Object} dependency manifest |
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've got no idea what a dependency manifest
is - an example might be great ;)
EDIT: Ah, now I see that it's the package.json
for that dependency :)
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.
Haha, I clarified it anyways.
scripts/clean-shrinkwrap.js
Outdated
.value(); | ||
|
||
try { | ||
return require(`.${path.sep}${manifestPath}`); |
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.
And an inline example comment of what this manifestPath
might look like would be useful too.
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.
Done!
scripts/clean-shrinkwrap.js
Outdated
* @example | ||
* const shrinkwrapFile = require('./npm-shrinkwrap.json'); | ||
* const dependencyTree = getDependencyTree(shrinkwrapFile, [ 'drivelist' ]); | ||
* const filteredShrinkwrap = removeDependencies(shrinkwrapFile, dependencyTree, { |
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 think this example is wrong, because getDependencyTree
returns Object[]
but the blacklist
parameter here is 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.
Oops, the type declaration was indeed wrong. Thanks for the heads up!
scripts/clean-shrinkwrap.js
Outdated
return _.compact(_.concat(accumulator, [ | ||
getPrettyShrinkwrapDependencyObject(shrinkwrapFile, dependencyPath) | ||
], getDependencyTree(shrinkwrapFile, dependencyPath))); | ||
}, []); |
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.
Should this chunk of code be moved into e.g. a buildBlacklistFromDependencyTree
function or something?
I'm afraid it looks pretty impenetrable to a non-Lodash-expert ;)
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.
Haha, what this does is basically taking every dependency in the black list, and for each of them, get an array of its dependencies + the dependency itself (hence _.concat
). We then flatten the whole thing as we go by using a fold.
I guess we can rename the internal bit to something like "get dependency tree AND the dependency itself". Maybe that could be just getTree()
?
scripts/clean-shrinkwrap.js
Outdated
* | ||
* @param {Object} shrinkwrap - the shrinkwrap file contents | ||
* @param {String[]} blacklist - dependency blacklist | ||
* @param {Object} qualifiers - qualifiers |
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.
Given the single-purpose nature of this script, I wonder if it'd be worth renaming removeDependencies
to removeOptionalDevDependencies
and then getting rid of the qualifiers
parameter? *shrug*
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.
Makes sense, done!
scripts/clean-shrinkwrap.js
Outdated
}), null, JSON_INDENTATION_SPACES); | ||
|
||
fs.writeFileSync(NPM_SHRINKWRAP_FILE_PATH, `${result}\n`); | ||
console.log('Done'); |
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.
console.log('Done, thank f***!');
😄
scripts/clean-shrinkwrap.js
Outdated
* level object, remove the blacklisted dependencies, and recurse | ||
* if possible. | ||
* | ||
* @param {Object} shrinkwrap - the shrinkwrap file contents |
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.
Does this parameter description need to be updated to reflect the recursive nature of this function? (or is that an internal-only detail?)
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.
Done!
It looks like a MinGW bug. Moving stuff using Node.js file-system works fine. We only use this for the CLI generation, which we will move to |
2a000ae
to
adac8c2
Compare
|
adac8c2
to
0d9bad5
Compare
Fixed! |
😢 |
scripts/clean-shrinkwrap.js
Outdated
|
||
// For example | ||
// ./node_modules/drivelist/node_modules/package.json | ||
return require(`.${path.sep}${manifestPath}`); |
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 not return require(path.join('.', manifestPath));
?
const path = require('path'); | ||
const os = require('os'); | ||
const packageJSON = require('../package.json'); |
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.
As (I think) I asked before, why use '../package.json'
here, but then path.join(__dirname, '..', 'npm-shrinkwrap.json')
a few lines below?
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 only need to require
the package.json, however we need to shrinkwrap path in two places: a require
call and the output of fs.writeFile
, so better to extract it as a constant to avoid path issues (require()
has special logic to understand ./
on Windows, but its not safe to assume that for fs.writeFile
as well)
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.
but its not safe to assume that for fs.writeFile as well)
Ahh, that's the bit I was missing, thanks. So require
definitely works with both slash-types on Windows?
scripts/clean-shrinkwrap.js
Outdated
* @example | ||
* const shrinkwrapFile = require('./npm-shrinkwrap.json'); | ||
* const dependencyTree = getDependencyTree(shrinkwrapFile, [ 'drivelist' ]); | ||
* const filteredShrinkwrap = removeDependencies(shrinkwrapFile, dependencyTree); |
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.
function-name in the example needs updating ;)
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.
Done!
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 example still says removeDependencies
whereas the function is named removeOptionalDevelopmentDependencies
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 forgot to push 😆
scripts/clean-shrinkwrap.js
Outdated
try { | ||
|
||
// For example | ||
// ./node_modules/drivelist/node_modules/package.json |
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.
Umm, should this be ./node_modules/drivelist/package.json
? Or maybe ./node_modules/drivelist/node_modules/lodash/package.json
?
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.
Right, fixed!
e789a69
to
2cc7b1f
Compare
Looks like I made a couple of subtle issues while addressing the review comments. It should work now. |
If we include a platform specific optional dependency in the shrinkwrap file, then npm will insist in installing it even if the platform doesn't match. As a solution, we figured out we can avoid putting this platform specific optional dependencies in the npm-shrinkwrap.json file. In order to do this, we currently have a script called `clean-shrinkwrap.js` that runs *before* any `npm shrinkwrap` file (its a `preshrinkwrap` npm script) that deletes all the platform specific modules we know about using `npm rm`. The problem with this approach is that `npm rm` will remove the module's code from `node_modules`, which means that if we run `npm shrinkwrap`, we will lose certain optional dependencies, that may be needed at a later stage. The solution is to modify the `clean-shrinkwrap.js` script to parse `npm-shrinkwrap.json`, and manually delete the entries that we want to omit. Also, the script needs to be run *after* `npm shrinkwrap`, so we change the npm script name to `postshrinkwrap`. Signed-off-by: Juan Cruz Viotti <[email protected]>
|
||
// For example | ||
// ./node_modules/drivelist/node_modules/lodash/package.json | ||
return require(`.${path.sep}${manifestPath}`); |
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.
Given your comment about require
in a comment just now, does this need to be .${path.sep}
instead of just ./
?
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.
It would work. Since manifestPath
is generated using path.join
, it may include backslashes, so I decided to use path.sep
just for consistency purposes (so we don't have paths like ./node_modules\package.json
).
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.
...so this line could also use path.join
for consistency purposes? ;)
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 really wanted to, but:
> path.join('.', 'foo')
'foo'
(path.join
omits the period, which require
needs to work properly, so we need to preppend path.sep
anyway)
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, thanks for the explanation! 👍
2cc7b1f
to
11f8127
Compare
Let's merge this to check if the logs are reduced enough to make Travis CI builds complete. |
If we include a platform specific optional dependency in the shrinkwrap
file, then npm will insist in installing it even if the platform doesn't
match. As a solution, we figured out we can avoid putting this platform
specific optional dependencies in the npm-shrinkwrap.json file.
In order to do this, we currently have a script called
clean-shrinkwrap.js
that runs before anynpm shrinkwrap
file (itsa
preshrinkwrap
npm script) that deletes all the platform specificmodules we know about using
npm rm
.The problem with this approach is that
npm rm
will remove the module'scode from
node_modules
, which means that if we runnpm shrinkwrap
,we will lose certain optional dependencies, that may be needed at a
later stage.
The solution is to modify the
clean-shrinkwrap.js
script to parsenpm-shrinkwrap.json
, and manually delete the entries that we want toomit. Also, the script needs to be run after
npm shrinkwrap
, so wechange the npm script name to
postshrinkwrap
.Signed-off-by: Juan Cruz Viotti [email protected]