Skip to content

Commit

Permalink
feat(core): add lock file to external nodes mapper
Browse files Browse the repository at this point in the history
  • Loading branch information
meeroslav committed Sep 29, 2022
1 parent 6452666 commit 202385b
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 70 deletions.
3 changes: 3 additions & 0 deletions packages/nx/src/config/project-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,16 @@ export interface ProjectGraphProjectNode<T = any> {

/**
* A node describing an external dependency
* `name` has as form of `npm:packageName` for primary dependencies or `npm:packageName@version` for hoisted ones
*/
export interface ProjectGraphExternalNode {
type: 'npm';
name: `npm:${string}`;
data: {
version: string;
packageName: string;
dependencies?: Record<string, [string, string]>; // dependencies of this version { [packageName]: [versionRange, actualVersion] }
peerDependencies?: Record<string, [string, string]>; // dependencies of this version { [packageName]: [versionRange, actualVersion] }
};
}

Expand Down
124 changes: 124 additions & 0 deletions packages/nx/src/utils/lock-file/__snapshots__/lock-file.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`lock-file mapLockFileDataToExternalNodes should map npm lock file data to external nodes 1`] = `
Object {
"data": Object {
"dependencies": Object {
"cliui": Array [
"^7.0.2",
"7.0.4",
],
"escalade": Array [
"^3.1.1",
"3.1.1",
],
"get-caller-file": Array [
"^2.0.5",
"2.0.5",
],
"require-directory": Array [
"^2.1.1",
"2.1.1",
],
"string-width": Array [
"^4.2.3",
"4.2.3",
],
"y18n": Array [
"^5.0.5",
"5.0.8",
],
"yargs-parser": Array [
"^21.0.0",
"21.0.1",
],
},
"packageName": "yargs",
"version": "17.5.1",
},
"name": "npm:yargs",
"type": "npm",
}
`;

exports[`lock-file mapLockFileDataToExternalNodes should map pnpm lock file data to external nodes 1`] = `
Object {
"data": Object {
"dependencies": Object {
"cliui": Array [
"7.0.4",
"7.0.4",
],
"escalade": Array [
"3.1.1",
"3.1.1",
],
"get-caller-file": Array [
"2.0.5",
"2.0.5",
],
"require-directory": Array [
"2.1.1",
"2.1.1",
],
"string-width": Array [
"4.2.3",
"4.2.3",
],
"y18n": Array [
"5.0.8",
"5.0.8",
],
"yargs-parser": Array [
"21.1.1",
"21.1.1",
],
},
"packageName": "yargs",
"version": "17.5.1",
},
"name": "npm:yargs",
"type": "npm",
}
`;

exports[`lock-file mapLockFileDataToExternalNodes should map yarn lock file data to external nodes 1`] = `
Object {
"data": Object {
"dependencies": Object {
"cliui": Array [
"^7.0.2",
"7.0.4",
],
"escalade": Array [
"^3.1.1",
"3.1.1",
],
"get-caller-file": Array [
"^2.0.5",
"2.0.5",
],
"require-directory": Array [
"^2.1.1",
"2.1.1",
],
"string-width": Array [
"^4.2.3",
"4.2.3",
],
"y18n": Array [
"^5.0.5",
"5.0.8",
],
"yargs-parser": Array [
"^21.0.0",
"21.1.1",
],
},
"packageName": "yargs",
"version": "17.5.1",
},
"name": "npm:yargs",
"type": "npm",
}
`;
26 changes: 16 additions & 10 deletions packages/nx/src/utils/lock-file/__snapshots__/pnpm.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
exports[`pnpm LockFile utility lock file with inline specifiers should parse lockfile (IS) 1`] = `
Object {
"@ampproject/[email protected]": Object {
"dependencies": Object {
"@jridgewell/gen-mapping": "0.1.1",
"@jridgewell/trace-mapping": "0.3.15",
},
"dev": true,
"engines": Object {
"node": ">=6.0.0",
},
Expand All @@ -13,7 +18,6 @@ Object {
"@jridgewell/gen-mapping": "0.1.1",
"@jridgewell/trace-mapping": "0.3.15",
},
"dev": true,
},
"isDependency": false,
"isDevDependency": false,
Expand All @@ -32,15 +36,14 @@ Object {
exports[`pnpm LockFile utility lock file with inline specifiers should parse lockfile (IS) 2`] = `
Object {
"[email protected]": Object {
"dev": true,
"engines": Object {
"node": ">=4.2.0",
},
"hasBin": true,
"packageMeta": Array [
Object {
"dependencyDetails": Object {
"dev": true,
"hasBin": true,
},
"dependencyDetails": Object {},
"isDependency": false,
"isDevDependency": true,
"key": "/typescript/4.8.3",
Expand All @@ -58,6 +61,11 @@ Object {
exports[`pnpm LockFile utility standard lock file should parse lockfile correctly 1`] = `
Object {
"@ampproject/[email protected]": Object {
"dependencies": Object {
"@jridgewell/gen-mapping": "0.1.1",
"@jridgewell/trace-mapping": "0.3.15",
},
"dev": true,
"engines": Object {
"node": ">=6.0.0",
},
Expand All @@ -68,7 +76,6 @@ Object {
"@jridgewell/gen-mapping": "0.1.1",
"@jridgewell/trace-mapping": "0.3.15",
},
"dev": true,
},
"isDependency": false,
"isDevDependency": false,
Expand All @@ -87,15 +94,14 @@ Object {
exports[`pnpm LockFile utility standard lock file should parse lockfile correctly 2`] = `
Object {
"[email protected]": Object {
"dev": true,
"engines": Object {
"node": ">=4.2.0",
},
"hasBin": true,
"packageMeta": Array [
Object {
"dependencyDetails": Object {
"dev": true,
"hasBin": true,
},
"dependencyDetails": Object {},
"isDependency": false,
"isDevDependency": true,
"key": "/typescript/4.8.3",
Expand Down
78 changes: 32 additions & 46 deletions packages/nx/src/utils/lock-file/lock-file.spec.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,40 @@
import { mapLockFileDataToExternalNodes } from './lock-file';
import { LockFileData } from './lock-file-type';
import { parseNpmLockFile } from './npm';
import { parsePnpmLockFile } from './pnpm';
import { parseYarnLockFile } from './yarn';
import { lockFileYargsOnly } from './__fixtures__/npm.lock';
import { lockFileYargsOnly as pnpmLockFileYargsOnly } from './__fixtures__/pnpm.lock';
import { lockFileDevkitAndYargs } from './__fixtures__/yarn.lock';

describe('lock-file', () => {
describe('mapLockFileDataToExternalNodes', () => {
it('should map lock file data to external nodes', () => {
const lockFileData: LockFileData = {
dependencies: {
'happy-nrwl': {
'[email protected]': {
version: '1.0.0',
resolved:
'https://registry.npmjs.org/happy-nrwl/-/happy-nrwl-1.0.0.tgz',
integrity: 'sha512-1',
packageMeta: ['[email protected]'],
},
},
'happy-nrwl2': {
'happy-nrwl2@^1.0.0': {
version: '1.2.0',
resolved:
'https://registry.npmjs.org/happy-nrwl2/-/happy-nrwl2-1.2.0.tgz',
integrity: 'sha512-2',
packageMeta: ['happy-nrwl@^1.0.0'],
},
},
},
};
const externalNodes = mapLockFileDataToExternalNodes(lockFileData);
expect(externalNodes).toEqual({
'npm:happy-nrwl': {
type: 'npm',
name: 'npm:happy-nrwl',
data: {
version: '1.0.0',
resolved:
'https://registry.npmjs.org/happy-nrwl/-/happy-nrwl-1.0.0.tgz',
integrity: 'sha512-1',
},
},
'npm:happy-nrwl2': {
type: 'npm',
name: 'npm:happy-nrwl2',
data: {
version: '1.2.0',
resolved:
'https://registry.npmjs.org/happy-nrwl2/-/happy-nrwl2-1.2.0.tgz',
integrity: 'sha512-2',
},
},
});
it('should map yarn lock file data to external nodes', () => {
const lockFileData = parseYarnLockFile(lockFileDevkitAndYargs);

const mappedExernalNodes = mapLockFileDataToExternalNodes(lockFileData);

expect(mappedExernalNodes['npm:yargs']).toMatchSnapshot();
});

it('should map npm lock file data to external nodes', () => {
const lockFileData = parseNpmLockFile(lockFileYargsOnly);

const mappedExernalNodes = mapLockFileDataToExternalNodes(lockFileData);

expect(mappedExernalNodes['npm:yargs']).toMatchSnapshot();
});

it('should map pnpm lock file data to external nodes', () => {
const lockFileData = parsePnpmLockFile(pnpmLockFileYargsOnly);

const mappedExernalNodes = mapLockFileDataToExternalNodes(lockFileData);

console.log(JSON.stringify(lockFileData.dependencies['yargs'], null, 2));

console.log(JSON.stringify(mappedExernalNodes['npm:yargs'], null, 2));

expect(mappedExernalNodes['npm:yargs']).toMatchSnapshot();
});
});
});
69 changes: 65 additions & 4 deletions packages/nx/src/utils/lock-file/lock-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import {
prunePnpmLockFile,
stringifyPnpmLockFile,
} from './pnpm';
import { LockFileData } from './lock-file-type';
import { LockFileData, PackageVersions } from './lock-file-type';
import { workspaceRoot } from '../workspace-root';
import { join } from 'path';
import { hashString } from './utils';
import { ProjectGraphExternalNode } from 'nx/src/config/project-graph';
import { findMatchingVersion, hashString } from './utils';
import { ProjectGraphExternalNode } from '../../config/project-graph';

/**
* Hashes lock file content
Expand Down Expand Up @@ -67,10 +67,71 @@ export function parseLockFile(
throw Error(`Unknown package manager: ${packageManager}`);
}

/**
* Maps lock file data to {@link ProjectGraphExternalNode} hash map
* @param lockFileData
* @returns
*/
export function mapLockFileDataToExternalNodes(
lockFileData: LockFileData
): Record<string, ProjectGraphExternalNode> {
return {};
const result: Record<string, ProjectGraphExternalNode> = {};
Object.keys(lockFileData.dependencies).forEach((dep) => {
Object.keys(lockFileData.dependencies[dep]).forEach((version, index) => {
const nodeName: `npm:${string}` = !index
? `npm:${dep}`
: `npm:${dep}@${version}`;
const packageVersion = lockFileData.dependencies[dep][version];

// map packages' transitive dependencies and peer dependencies to external nodes' versions
const dependencies = mapTransitiveDependencies(
lockFileData.dependencies,
packageVersion.dependencies
);
const peerDependencies = mapTransitiveDependencies(
lockFileData.dependencies,
packageVersion.peerDependencies
);

// save external node
result[nodeName] = {
type: 'npm',
name: nodeName,
data: {
version: packageVersion.version,
packageName: dep,
...(dependencies && { dependencies }),
...(peerDependencies && { peerDependencies }),
},
};
});
});
return result;
}

// Finds the maching version of each dependency of the package and
// maps each {package}:{versionRange} pair to {package}:[{versionRange}, {version}]
function mapTransitiveDependencies(
packages: Record<string, PackageVersions>,
dependencies: Record<string, string>
): Record<string, [string, string]> {
if (!dependencies) {
return undefined;
}
const result: Record<string, [string, string]> = {};

Object.keys(dependencies).forEach((packageName) => {
const version = findMatchingVersion(
packages[packageName],
dependencies[packageName]
);
result[packageName] = [
dependencies[packageName],
version || dependencies[packageName],
];
});

return result;
}

/**
Expand Down
Loading

0 comments on commit 202385b

Please sign in to comment.