Skip to content

Commit

Permalink
Add support for yarn-berry lockfiles
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeduminy committed Jul 4, 2023
1 parent e18680c commit 129deff
Show file tree
Hide file tree
Showing 15 changed files with 805 additions and 36 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
# Generated files
*.log
node_modules/
.yarn/
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"main": "src/cli.js",
"scripts": {
"test": "jest",
"check-format": "prettier --check {src,test}/**/*.js README.md",
"format": "prettier --write {src,test}/**/*.js README.md"
"check-format": "prettier --check \"{src,test}/**/*.js\" README.md",
"format": "prettier --write \"{src,test}/**/*.js\" README.md"
},
"bin": {
"diglett": "./bin/diglett"
Expand Down Expand Up @@ -40,6 +40,7 @@
"@yarnpkg/lockfile": "^1.1.0",
"chalk": "^3.0.0",
"glob": "^7.1.6",
"js-yaml": "^4.1.0",
"yargs": "^15.1.0"
},
"devDependencies": {
Expand Down
1 change: 0 additions & 1 deletion src/commands/yarn-workspace.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const sharedArguments = require('../sharedArguments');
Expand Down
2 changes: 0 additions & 2 deletions src/errors.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
const path = require('path');

class DiglettError extends Error {
constructor(message, errorType) {
super(message);
Expand Down
19 changes: 15 additions & 4 deletions src/fs.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const fs = require('fs');
const path = require('path');
const lockfile = require('@yarnpkg/lockfile');
const yml = require('js-yaml');
const { FileNotFoundError, ParseError } = require('./errors');

function readFile(fileName, projectPath) {
Expand All @@ -22,11 +23,21 @@ function readJSON(fileName, projectPath) {

function readYarnLockfile(projectPath) {
const file = readFile('yarn.lock', projectPath);
const parsed = lockfile.parse(file);
if (parsed.type !== 'success') {
throw new ParseError('Failed to parse yarn.lock');

try {
const parsed = lockfile.parse(file);
if (parsed.type !== 'success') {
throw new ParseError('Failed to parse yarn.lock');
}
return parsed.object;
} catch {
try {
const parsed = yml.load(file);
return parsed;
} catch (error) {
throw new ParseError('Failed to parse yarn.lock');
}
}
return parsed.object;
}

function readPackageJSON(projectPath) {
Expand Down
2 changes: 0 additions & 2 deletions src/getDuplicateDependencies.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
const groupYarnDependencies = require('./groupYarnDependencies');

function getDuplicateDependencies(groupedVersions, packageNamePattern) {
const duplicates = new Map();
groupedVersions.forEach((versions, packageName) => {
Expand Down
32 changes: 28 additions & 4 deletions src/groupYarnDependencies.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
const { StaleLockfileError } = require('./errors');
const parseYarnDescriptor = require('./parseYarnDescriptor');

function resolveDependency(packageName, requestedVersion, dependencies) {
// yarn v1 uses a simple format for the key: `${packageName}@${version}`
const simpleKey = `${packageName}@${requestedVersion}`;
if (simpleKey in dependencies) return dependencies[simpleKey];

for (const [key, value] of Object.entries(dependencies)) {
// ignore metadata keys
if (key === '__metadata') continue;

// yarn v2 uses a more complex format for the key: `${packageName}@${protocol}${version}`
const { packageName: name, version } = parseYarnDescriptor(key);
if (name === packageName && version === requestedVersion) {
return value;
}
}

return null;
}

function groupYarnDependencies(
packageDependencies,
Expand All @@ -16,18 +36,22 @@ function groupYarnDependencies(
}

const versions = installedVersions.get(packageName);
const dependencyKey = `${packageName}@${requestedVersion}`;
const resolvedDependency = dependencies[dependencyKey];
const resolvedDependency = resolveDependency(
packageName,
requestedVersion,
dependencies
);

if (!resolvedDependency) {
throw new StaleLockfileError(
`Unable to find resolution for "${dependencyKey}", ensure yarn.lock is up to date`
`Unable to find resolution for package "${packageName}" and version "${requestedVersion}", ensure yarn.lock is up to date`
);
}
const installedVersion = resolvedDependency.version;

if (!versions.has(installedVersion)) {
versions.add(installedVersion);
const subDependencies = dependencies[dependencyKey].dependencies;
const subDependencies = resolvedDependency.dependencies;
for (const subDependency in subDependencies) {
populateVersions(
subDependency,
Expand Down
41 changes: 41 additions & 0 deletions src/parseYarnDescriptor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* A regular expression for parsing a yarn berry descriptor.
*/
const PARSE_REGEX = /(^@?[^/]+?\/?[^@/]+?)@(?:.*:)*(.+)/;

/**
* @typedef {Object} ParsedDescriptor
* @property {string} packageName
* @property {string} version
*/

/**
* Parses a yarn berry descriptor into an object.
*
* For example `@material/ripple@npm:1.0.0` will be parsed to:
* ```
* {
* package: '@material/ripple',
* version: '1.0.0',
* }
* ```
*
* Supported formats:
* - `package@version`
* - `@scope/package@version`
* - `package@protocol:version`
* - `@scope/package@protocol:version`
*
* @param {string} descriptor - The yarn berry descriptor to parse.
* @returns {ParsedDescriptor} The parsed descriptor.
*/
function parseYarnDescriptor(descriptor) {
const result = PARSE_REGEX.exec(descriptor);
if (!result) {
throw new Error(`Unable to parse descriptor: ${descriptor}`);
}
const [, packageName, version] = result;
return { packageName, version };
}

module.exports = parseYarnDescriptor;
1 change: 1 addition & 0 deletions test/fixtures/yarn-berry/.yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
Loading

0 comments on commit 129deff

Please sign in to comment.