vasilevich / sync-yarnlock-into-packagejson

MIT License
111 stars 14 forks source link

Support for Yarn 2+ / Berry #23

Open mxro opened 2 years ago

mxro commented 2 years ago

Would this tool work for Yarn v2+ / Berry (https://yarnpkg.com/)?

rndD commented 1 year ago

I made a script to sync versions for my yarn3 monorepo https://gist.github.com/rndD/044b6a00715710cdabf08d70452dbedb . Feel free to use it. I'm too lazy to make a package.

mxro commented 1 year ago

Nice script! I guess this could be added to this package?

laurent22 commented 1 year ago

Thanks for sharing @rndD, it works great. I've modified it to also support dev dependencies:

const fs =require('fs');
import { parseSyml } from '@yarnpkg/parsers';
const path = require('path');

let file = fs.readFileSync('yarn.lock', 'utf8');
let json = parseSyml(file);

// function make map with all packages and their versions. 
// Json format:
// 'zen-observable-ts@npm:^1.2.0': {
//   version: '1.2.3',
//   resolution: 'zen-observable-ts@npm:1.2.3',
//   dependencies: { 'zen-observable': '0.8.15' },
//   checksum: '0548b555c67671f1240fb416755d2c27abf095b74a9e25c1abf23b2e15de40e6b076c678a162021358fe62914864eb9f0a57cd65e203d66c4988a08b220e6172',
//   languageName: 'node',
//   linkType: 'hard'
// },
const packagesInYarn = new Map();

for (const key of Object.keys(json)) {
  const packageNVersions = key.split(',');
  packageNVersions.forEach((packageNVersion) => {
    packageNVersion = packageNVersion.trim();
    const [packageName, version] = packageNVersion.split('@npm:');
    const resolvedVersion = json[key].version;
    packagesInYarn.set(`${packageName}|${version}`, resolvedVersion);
  });
}

const processDeps = (deps:any) => {
  let changed = false;

  if (deps) {
    Object.keys(deps).forEach((dep) => {
      if (packagesInYarn.has(`${dep}|${deps[dep]}`)) {
        const resolvedVersion = packagesInYarn.get(`${dep}|${deps[dep]}`);
        if (resolvedVersion !== deps[dep]) {
          deps[dep] = resolvedVersion;
          changed = true;
        }
      }
    });
  }

  return changed;
}

function findDivergeVersionsInPackageJson(rootDir: string) {
  // read all package json in root except node_modules
  const files = fs.readdirSync(rootDir);
  for (const file of files) {
    const filePath = path.join(rootDir, file);
    const stat = fs.statSync(filePath);
    if (stat.isDirectory() && file !== 'node_modules') {
      findDivergeVersionsInPackageJson(filePath);
    } else if (stat.isFile() && file === 'package.json') {
      const packageJson = fs.readFileSync(filePath, 'utf8');
      const parsedPackageJson = parseSyml(packageJson);
      const {
        dependencies: deps,
      } = parsedPackageJson;

      const changedDeps = processDeps(deps);
      const changedDevDeps = processDeps(parsedPackageJson.devDependencies);

      if (changedDeps || changedDevDeps) {
        fs.writeFileSync(
          filePath,
          JSON.stringify(parsedPackageJson, null, 2) + `\r`,
        );
      }
    }
  }
}

findDivergeVersionsInPackageJson(process.cwd());
rndD commented 1 year ago

@laurent22 Nice! Thanks for sharing.

jeremybacher commented 1 year ago

Thanks for sharing, that solution works for me @rndD @laurent22 !! 💯

I'm also share, the change of what I have to do to work in my project:

In package.json:

"scripts": {
    "lock": "yarn add @yarnpkg/parsers && npx ts-node yarn-lock-versions-to-package.ts"
}

Create new file (yarn-lock-versions-to-package.ts) and paste the following:

const fs = require('fs');
const parseSyml = require('@yarnpkg/parsers').parseSyml;
const path = require('path');

let file = fs.readFileSync('yarn.lock', 'utf8');
let json = parseSyml(file);

const packagesInYarn = new Map();

for (const key of Object.keys(json)) {
  const packageNVersions = key.split(',');
  packageNVersions.forEach((packageNVersion) => {
    packageNVersion = packageNVersion.trim();
    const [packageName, version] = packageNVersion.split('@npm:');
    const resolvedVersion = json[key].version;
    packagesInYarn.set(`${packageName}|${version}`, resolvedVersion);
  });
}

const processDeps = (deps:any) => {
  let changed = false;

  if (deps) {
    Object.keys(deps).forEach((dep) => {
      if (packagesInYarn.has(`${dep}|${deps[dep]}`)) {
        const resolvedVersion = packagesInYarn.get(`${dep}|${deps[dep]}`);
        if (resolvedVersion !== deps[dep]) {
          deps[dep] = resolvedVersion;
          changed = true;
        }
      }
    });
  }

  return changed;
}

function findDivergeVersionsInPackageJson(rootDir: string) {
  // read all package json in root except node_modules
  const files = fs.readdirSync(rootDir);
  for (const file of files) {
    const filePath = path.join(rootDir, file);
    const stat = fs.statSync(filePath);
    if (stat.isDirectory() && file !== 'node_modules') {
      findDivergeVersionsInPackageJson(filePath);
    } else if (stat.isFile() && file === 'package.json') {
      const packageJson = fs.readFileSync(filePath, 'utf8');
      const parsedPackageJson = parseSyml(packageJson);
      const {
        dependencies: deps,
      } = parsedPackageJson;

      const changedDeps = processDeps(deps);
      const changedDevDeps = processDeps(parsedPackageJson.devDependencies);

      if (changedDeps || changedDevDeps) {
        fs.writeFileSync(
          filePath,
          JSON.stringify(parsedPackageJson, null, 2) + `\r`,
        );
      }
    }
  }
}

findDivergeVersionsInPackageJson(process.cwd());

Finally, execute: yarn lock.

Hope it works for you too!