JamieMason / shrinkpack

Fast, resilient, reproducible builds with npm install.
https://www.npmjs.com/package/shrinkpack
MIT License
793 stars 38 forks source link

Add guides/recipes #91

Closed JamieMason closed 2 years ago

JamieMason commented 6 years ago

Either as part of the documentation or the CLI, add interactive guides/recipes for;

while updating the lockfile and shrinkpack in a clean and predictable way, avoiding common gotchas.

An analogy I like to use is that a lockfile is a bit like a cake lid. You take it off while you're making changes then put it back after you've finished changing it and want to protect it from the outside world.

JamieMason commented 6 years ago

I don't know about other people but I always tend to rm -rf ./package-lock.json ./npm-shrinkwrap.json node_modules and do a full reinstall.

Trying to make changes while the "cake lid" is still covering the project can expose you to whatever potential problems may lie in npm's resolution logic. Nuking and regenerating from scratch has always resulted in a clean, valid install for me.

valscion commented 6 years ago

We try to avoid nuking npm-shrinkwrap.json and node_modules at @venuu if possible, and figure out what packages we'd need to reinstall to create a clean result.

...although, the reason why it might work is that we're still using npm@2 😅 . Looking forward to npm@5 support, as v3 and v4 weren't quite satisfactory for us yet :)

JamieMason commented 6 years ago

and figure out what packages we'd need to reinstall to create a clean result

Can you expand on this? I don't follow sorry, can you explain the scenario?

valscion commented 6 years ago

Yeah, sorry to leave just a drive-by comment 😅 . Had a hurry to review another PR in the meantime.

Anyway, the approach we've used to upgrade a package is to remove it, install a newer version and shrinkpack it. Then we'd make other team members uninstall the same package before reinstalling from a clean shrinkpack.

Sometimes this method might cause drift between different developer's machines, though, where some sub-sub-dependency is moved to a different part of the module tree.

Basically,

npm uninstall babel-plugin-transform-class-properties
npm install babel-plugin-transform-class-properties@latest
npm run shrink
# The `shrink` npm script does this:
#
#   npm prune && npm shrinkwrap --silent && npm run shrinkpack && ./script/clean-shrinkwrap.js
#
# ...where the `shrinkpack` script is just `"shrinkpack": "shrinkpack"`

We're also using a slightly outdated version of shrinkpack (https://github.com/venuu/shrinkpack/commit/1b92aa158eb6828075e7d20b58cd434568e6c5ea) but it shouldn't matter here.

valscion commented 6 years ago

Oh, and the ./script/clean-shrinkwrap.js script looks like this:

#!/usr/bin/env node

/**
 * this script is just a temporary solution to deal with the issue of npm outputting the npm
 * shrinkwrap file in an unstable manner.
 *
 * See: https://github.com/npm/npm/issues/3581
 */

const _ = require('lodash');
const fs = require('fs');
const path = require('path');

function cleanModule(module, _name) {
  delete module.from;

  _.forEach(module.dependencies, (mod, dependencyName) =>
    cleanModule(mod, dependencyName)
  );
}

function deepSorted(objToSort) {
  return Object.keys(objToSort)
    .sort()
    .reduce((result, key) => {
      const current = objToSort[key];
      if (typeof current === 'object') {
        result[key] = deepSorted(current);
      } else {
        result[key] = current;
      }
      return result;
    }, {});
}

console.log('Reading npm-shrinkwrap.json');
const shrinkwrap = require('../npm-shrinkwrap.json');

console.log('Cleaning shrinkwrap object');
cleanModule(shrinkwrap, shrinkwrap.name);

const cleanShrinkwrapPath = path.join(__dirname, '..', 'npm-shrinkwrap.json');
console.log('Writing cleaned to', cleanShrinkwrapPath);

fs.writeFileSync(
  cleanShrinkwrapPath,
  `${JSON.stringify(deepSorted(shrinkwrap), null, 2)}\n`
);

Basically, it avoids npm shuffling shrinkwrap contents by sorting them, and removes fields from shrinkpack file that are different if the package is installed via a local copy or resolved from a remote repository.

JamieMason commented 6 years ago

Then we'd make other team members uninstall the same package before reinstalling from a clean shrinkpack

Just to verify that the install works? ok makes sense.

it avoids npm shuffling shrinkwrap contents by sorting them, and removes fields from shrinkpack file that are different if the package is installed via a local copy or resolved from a remote repository.

From npm5 there is no from field in the shrinkwrap so that will be a bonus. I like the idea of sorting the keys to try and keep noise in diffs to a minimum, maybe shrinkpack could do this.

Thanks a lot for explaining!

valscion commented 6 years ago

Just to verify that the install works? ok makes sense.

Oh, it's to verify that when they run npm run shrink again, they'd get no diff. Usually the small differences there'd be won't cause functional differences, not anything large anyway.

From npm5 there is no from field in the shrinkwrap so that will be a bonus

That's a great thing to know, thanks!

I like the idea of sorting the keys to try and keep noise in diffs to a minimum, maybe shrinkpack could do this.

Could be — it has always been a bit weird that built-in shrinkwrap doesn't do this already.

Thanks a lot for explaining!

My pleasure! 🙇