Skip to content

Instantly share code, notes, and snippets.

@jzaefferer
Created February 18, 2021 15:20
Show Gist options
  • Save jzaefferer/39bd074b5a448cace1e3fe9f7c57e2b4 to your computer and use it in GitHub Desktop.
Save jzaefferer/39bd074b5a448cace1e3fe9f7c57e2b4 to your computer and use it in GitHub Desktop.
Automatically check if installed packages are up-to-date, before start and lint scripts, and on `git pull`; MIT License, Copyright Jörn Zaefferer
#!/usr/bin/env node
//@ts-check
const { execSync } = require('child_process')
const mainPackage = require('../package.json')
const packageLock = require('../package-lock.json')
const outdated = []
Object.keys(mainPackage.dependencies)
.concat(Object.keys(mainPackage.devDependencies))
.forEach(packageName => {
try {
const installedPackage = require(`${packageName}/package.json`)
const installedVersion = installedPackage.version
const desiredVersion = packageLock.dependencies[packageName].version
if (installedVersion !== desiredVersion) {
outdated.push(
`${packageName}:, installed ${installedVersion}, desired ${desiredVersion}`
)
}
} catch (error) {
if (error.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
console.error(error)
process.exit(1)
}
}
})
if (outdated.length > 0) {
console.log(`some package(s) are outdated, updating`)
console.log(outdated.join('\n'))
execSync(`npm i --registry=https://registry.npmjs.org/`)
}
{
"other": "stuff,
"scripts": {
"prelint": "node scripts/check-packages.js",
"prestart": "node scripts/check-packages.js"
},
"husky": {
"hooks": {
"post-merge": "node scripts/check-packages.js"
}
},
}
@jzaefferer
Copy link
Author

This sounds promising, but I'm not sure if it's really equivalent. Does yarn.lock really know what's currently installed? Or only what's supposed to be installed? The latter applies to package-lock.json, and that's the reason for checking every individual dependency.

@manuschillerdev
Copy link

manuschillerdev commented Feb 20, 2021

ah, I see - thanks! I misunderstood the use case.
so basically for yarn it's the same as you and your team did:

const { parse } = require("@yarnpkg/lockfile");
const { readFileSync } = require("fs");
const { execSync } = require("child_process");

const yarnLock = readFileSync("./yarn.lock", "utf8");
const { object: lockedDependencies } = parse(yarnLock);

const pkg = require("./package.json");
const dependencies = {
  ...(pkg.dependencies || {}),
  ...(pkg.devDependencies || {}),
};

const install = () => execSync("yarn");

for (const [name, version] of Object.entries(dependencies)) {
  try {
    const installed = require(`${name}/package.json`);
    const locked = lockedDependencies[`${name}@${version}`];
    if (!locked || installed.version !== locked.version) {
      install();
      break;
    }
  } catch (e) {
    if (e.code === "MODULE_NOT_FOUND") install();
    else throw e;
  }
}

Would be cool if we turned this into a module that supports npm, yarn and pnpm probably!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment