Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"packageManager": "pnpm@8.15.9+sha512.499434c9d8fdd1a2794ebf4552b3b25c0a633abcee5bb15e7b5de90f32f47b513aca98cd5cfd001c31f0db454bc3804edccd578501e4ca293a6816166bbd9f81"
}
15 changes: 8 additions & 7 deletions packages/pnpm-sync-lib/package.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
{
"name": "pnpm-sync-lib",
"version": "0.3.0",
"version": "0.3.1",
"description": "API library for integrating \"pnpm-sync\" with your toolchain",
"repository": {
"type": "git",
"url": "https://github.com/tiktok/pnpm-sync.git",
"directory": "packages/pnpm-sync-lib"
},
"license": "MIT",
"main": "lib/index.js",
"types": "dist/pnpm-sync-lib.d.ts",
"license": "MIT",
"scripts": {
"build": "heft build --clean",
"test": "heft test --clean",
"lint": "eslint src/**",
"prepublishOnly": "heft build --clean"
"prepublishOnly": "heft build --clean",
"test": "heft test --clean"
},
"dependencies": {
"@pnpm/dependency-path": "2.1.8",
"@pnpm/dependency-path-2": "npm:@pnpm/dependency-path@^2.1.8",
"@pnpm/dependency-path-5": "npm:@pnpm/dependency-path@^5.1.7",
"yaml": "2.4.1"
},
"devDependencies": {
Expand All @@ -27,7 +28,7 @@
"@rushstack/heft-node-rig": "^2.4.5",
"@types/heft-jest": "1.0.6",
"@types/node": "18.17.15",
"typescript": "~5.3.3",
"eslint": "~8.7.0"
"eslint": "~8.7.0",
"typescript": "~5.3.3"
}
}
97 changes: 60 additions & 37 deletions packages/pnpm-sync-lib/src/pnpmSyncPrepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import path from 'path';
import fs from 'fs';
import YAML from 'yaml';
import process from 'node:process';
import { depPathToFilename } from '@pnpm/dependency-path';
import { depPathToFilename as depPathToFilename2 } from '@pnpm/dependency-path-2';
import { depPathToFilename as depPathToFilename5 } from '@pnpm/dependency-path-5';

import {
ILockfile,
Expand All @@ -11,7 +12,6 @@ import {
LogMessageIdentifier,
IPnpmSyncJson,
IVersionSpecifier,
ILockfilePackage,
ITargetFolder
} from './interfaces';
import { pnpmSyncGetJsonVersion } from './utilities';
Expand Down Expand Up @@ -220,9 +220,9 @@ export async function pnpmSyncPrepareAsync(options: IPnpmSyncPrepareOptions): Pr
const pnpmVersion: string | undefined = pnpmModulesYaml?.packageManager?.split('@')[1];

// currently, only support pnpm v8
if (!pnpmVersion || !pnpmVersion.startsWith('8')) {
if (!pnpmVersion || !(pnpmVersion.startsWith('8') || !pnpmVersion.startsWith('9'))) {
logMessageCallback({
message: `The pnpm version is not supported; pnpm-sync requires pnpm version 8.x`,
message: `The pnpm version is not supported; pnpm-sync requires pnpm version 8.x, 9.x`,
messageKind: LogMessageKind.ERROR,
details: {
messageIdentifier: LogMessageIdentifier.PREPARE_ERROR_UNSUPPORTED_PNPM_VERSION,
Expand All @@ -240,9 +240,13 @@ export async function pnpmSyncPrepareAsync(options: IPnpmSyncPrepareOptions): Pr

// currently, only support lockfileVersion 6.x, which is pnpm v8
const lockfileVersion: string | undefined = pnpmLockfile?.lockfileVersion.toString();
if (!lockfileVersion || !lockfileVersion.startsWith('6')) {
if (
!pnpmLockfile ||
!lockfileVersion ||
!(lockfileVersion.startsWith('6') || !lockfileVersion.startsWith('9'))
) {
logMessageCallback({
message: `The pnpm-lock.yaml format is not supported; pnpm-sync requires lockfile version 6`,
message: `The pnpm-lock.yaml format is not supported; pnpm-sync requires lockfile version 6, 9`,
messageKind: LogMessageKind.ERROR,
details: {
messageIdentifier: LogMessageIdentifier.PREPARE_ERROR_UNSUPPORTED_FORMAT,
Expand All @@ -255,7 +259,7 @@ export async function pnpmSyncPrepareAsync(options: IPnpmSyncPrepareOptions): Pr

// find injected dependency and all its available versions
const injectedDependencyToVersion: Map<string, Set<string>> = new Map();
for (const importerItem of Object.values(pnpmLockfile?.importers || {})) {
for (const importerItem of Object.values(pnpmLockfile.importers || {})) {
// based on https://pnpm.io/package_json#dependenciesmeta
// the injected dependencies could available inside dependencies, optionalDependencies, and devDependencies.
getInjectedDependencyToVersion(importerItem?.dependencies, injectedDependencyToVersion);
Expand All @@ -282,12 +286,17 @@ export async function pnpmSyncPrepareAsync(options: IPnpmSyncPrepareOptions): Pr
injectedDependencyToFilePathSet.set(injectedDependencyPath, []);
}

const fullPackagePath = path.join(
dotPnpmFolder,
depPathToFilename(injectedDependencyVersion),
'node_modules',
injectedDependency
);
const packageDirname = (() => {
if (pnpmVersion.startsWith('8')) {
return depPathToFilename2(injectedDependencyVersion);
}
if (pnpmVersion.startsWith('9')) {
return depPathToFilename5(injectedDependency + '@' + injectedDependencyVersion, 120);
}
return '';
})();

const fullPackagePath = path.join(dotPnpmFolder, packageDirname, 'node_modules', injectedDependency);

injectedDependencyToFilePathSet.get(injectedDependencyPath)?.push(fullPackagePath);
}
Expand Down Expand Up @@ -373,50 +382,64 @@ function getInjectedDependencyToVersion(
// process all dependencies and devDependencies to find potential transitive injected dependencies
// and add to injectedDependencyToFilePath map
function processTransitiveInjectedDependency(
pnpmLockfile: ILockfile | undefined,
pnpmLockfile: ILockfile,
injectedDependencyToVersion: Map<string, Set<string>>
): void {
const { lockfileVersion } = pnpmLockfile;

const potentialTransitiveInjectedDependencyVersionQueue: Array<string> = [];
for (const injectedDependencyVersion of [...injectedDependencyToVersion.values()]) {
potentialTransitiveInjectedDependencyVersionQueue.push(...injectedDependencyVersion);
for (const [packageName, injectedDependencyVersion] of [...injectedDependencyToVersion.entries()]) {
if (lockfileVersion.toString().startsWith('6')) {
potentialTransitiveInjectedDependencyVersionQueue.push(...injectedDependencyVersion);
} else if (lockfileVersion.toString().startsWith('9')) {
potentialTransitiveInjectedDependencyVersionQueue.push(
...[...injectedDependencyVersion].map((version) => packageName + '@' + version)
);
}
}

const lockfilePackages: Record<string, ILockfilePackage> | undefined = pnpmLockfile?.packages;

if (lockfilePackages) {
while (potentialTransitiveInjectedDependencyVersionQueue.length > 0) {
const transitiveInjectedDependencyVersion: string | undefined =
potentialTransitiveInjectedDependencyVersionQueue.shift();
if (transitiveInjectedDependencyVersion) {
const { dependencies, optionalDependencies } = lockfilePackages[transitiveInjectedDependencyVersion];
processInjectedDependencies(
dependencies,
injectedDependencyToVersion,
potentialTransitiveInjectedDependencyVersionQueue
);
processInjectedDependencies(
optionalDependencies,
injectedDependencyToVersion,
potentialTransitiveInjectedDependencyVersionQueue
);
}
const { packages: lockfilePackages } = pnpmLockfile;

while (potentialTransitiveInjectedDependencyVersionQueue.length > 0) {
const transitiveInjectedDependencyVersion: string | undefined =
potentialTransitiveInjectedDependencyVersionQueue.shift();
if (transitiveInjectedDependencyVersion) {
const { dependencies, optionalDependencies } = lockfilePackages[transitiveInjectedDependencyVersion];
processInjectedDependencies(
dependencies,
injectedDependencyToVersion,
potentialTransitiveInjectedDependencyVersionQueue,
pnpmLockfile
);
processInjectedDependencies(
optionalDependencies,
injectedDependencyToVersion,
potentialTransitiveInjectedDependencyVersionQueue,
pnpmLockfile
);
}
}
}
function processInjectedDependencies(
dependencies: Record<string, string> | undefined,
injectedDependencyToVersion: Map<string, Set<string>>,
potentialTransitiveInjectedDependencyVersionQueue: Array<string>
potentialTransitiveInjectedDependencyVersionQueue: Array<string>,
pnpmLockfile: ILockfile
): void {
if (dependencies) {
const { lockfileVersion } = pnpmLockfile;
for (const [dependency, version] of Object.entries(dependencies)) {
// if the version is set with file: protocol, then it is a transitive injected dependency
if (version.startsWith('file:')) {
if (!injectedDependencyToVersion.has(dependency)) {
injectedDependencyToVersion.set(dependency, new Set());
}
injectedDependencyToVersion.get(dependency)?.add(version);
potentialTransitiveInjectedDependencyVersionQueue.push(version);
if (lockfileVersion.toString().startsWith('6')) {
potentialTransitiveInjectedDependencyVersionQueue.push(version);
} else if (lockfileVersion.toString().startsWith('9')) {
potentialTransitiveInjectedDependencyVersionQueue.push(dependency + '@' + version);
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/pnpm-sync/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pnpm-sync",
"version": "0.2.9",
"version": "0.3.1",
"description": "Recopy injected dependencies whenever a project is rebuilt in your PNPM workspace",
"keywords": [
"rush",
Expand Down
44 changes: 41 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/pnpm-sync-api-test/src/test/pnpmSyncPrepare.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ Array [
Object {
"details": Object {
"actualVersion": "incompatible-version",
"expectedVersion": "0.3.0",
"expectedVersion": "0.3.1",
"messageIdentifier": "prepare-replacing-file",
"pnpmSyncJsonPath": "<root>/pnpm-sync/tests/test-fixtures/sample-lib1/node_modules/.pnpm-sync.json",
"sourceProjectFolder": "<root>/pnpm-sync/tests/test-fixtures/sample-lib1",
Expand Down