Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support version in .pnpm-sync.json #23

Merged
merged 4 commits into from
Apr 9, 2024
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
20 changes: 19 additions & 1 deletion packages/pnpm-sync-lib/etc/pnpm-sync-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,13 @@ export type LogMessageDetails = {
} | {
messageIdentifier: LogMessageIdentifier.PREPARE_PROCESSING;
lockfilePath: string;
dotPnpmFolderPath: string;
dotPnpmFolder: string;
} | {
messageIdentifier: LogMessageIdentifier.PREPARE_REPLACING_FILE;
pnpmSyncJsonPath: string;
projectFolder: string;
actualVersion: string;
expectedVersion: string;
} | {
messageIdentifier: LogMessageIdentifier.PREPARE_WRITING_FILE;
pnpmSyncJsonPath: string;
Expand All @@ -102,6 +108,11 @@ export type LogMessageDetails = {
} | {
messageIdentifier: LogMessageIdentifier.COPY_ERROR_NO_SYNC_FILE;
pnpmSyncJsonPath: string;
} | {
messageIdentifier: LogMessageIdentifier.COPY_ERROR_INCOMPATIBLE_SYNC_FILE;
pnpmSyncJsonPath: string;
actualVersion: string;
expectedVersion: string;
} | {
messageIdentifier: LogMessageIdentifier.COPY_FINISHING;
pnpmSyncJsonPath: string;
Expand All @@ -112,6 +123,8 @@ export type LogMessageDetails = {

// @beta (undocumented)
export enum LogMessageIdentifier {
// (undocumented)
COPY_ERROR_INCOMPATIBLE_SYNC_FILE = "copy-error-incompatible-sync-file",
// (undocumented)
COPY_ERROR_NO_SYNC_FILE = "copy-error-no-sync-file",
// (undocumented)
Expand All @@ -125,6 +138,8 @@ export enum LogMessageIdentifier {
// (undocumented)
PREPARE_PROCESSING = "prepare-processing",
// (undocumented)
PREPARE_REPLACING_FILE = "prepare-replacing-file",
// (undocumented)
PREPARE_STARTING = "prepare-starting",
// (undocumented)
PREPARE_WRITING_FILE = "prepare-writing-file"
Expand All @@ -145,6 +160,9 @@ export enum LogMessageKind {
// @beta
export function pnpmSyncCopyAsync(options: IPnpmSyncCopyOptions): Promise<void>;

// @beta
export function pnpmSyncGetJsonVersion(): string;

// @beta
export function pnpmSyncPrepareAsync(options: IPnpmSyncPrepareOptions): Promise<void>;

Expand Down
2 changes: 1 addition & 1 deletion packages/pnpm-sync-lib/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pnpm-sync-lib",
"version": "0.2.0",
"version": "0.2.1",
"description": "API library for integrating \"pnpm-sync\" with your toolchain",
"repository": {
"type": "git",
Expand Down
1 change: 1 addition & 0 deletions packages/pnpm-sync-lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
export { pnpmSyncCopyAsync, type IPnpmSyncCopyOptions } from './pnpmSyncCopy';
export { pnpmSyncPrepareAsync, type IPnpmSyncPrepareOptions } from './pnpmSyncPrepare';
export { LogMessageIdentifier, LogMessageKind, LogMessageDetails } from './interfaces';
export { pnpmSyncGetJsonVersion } from './utilities';
export type {
ILockfile,
ILockfileImporter,
Expand Down
18 changes: 17 additions & 1 deletion packages/pnpm-sync-lib/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface IPnpmSyncCliArgs {
}

export interface IPnpmSyncJson {
version: string;
postbuildInjectedCopy: {
sourceFolder: string;
targetFolders: Array<ITargetFolder>;
Expand Down Expand Up @@ -33,12 +34,14 @@ export enum LogMessageIdentifier {
PREPARE_STARTING = 'prepare-starting',
PREPARE_ERROR_UNSUPPORTED_FORMAT = 'prepare-error-unsupported-format',
PREPARE_PROCESSING = 'prepare-processing',
PREPARE_REPLACING_FILE = 'prepare-replacing-file',
PREPARE_WRITING_FILE = 'prepare-writing-file',
PREPARE_FINISHING = 'prepare-finishing',

// pnpmSyncCopyAsync() messages
COPY_STARTING = 'copy-starting',
COPY_ERROR_NO_SYNC_FILE = 'copy-error-no-sync-file',
COPY_ERROR_INCOMPATIBLE_SYNC_FILE = 'copy-error-incompatible-sync-file',
COPY_FINISHING = 'copy-finishing'
}

Expand All @@ -59,7 +62,14 @@ export type LogMessageDetails =
| {
messageIdentifier: LogMessageIdentifier.PREPARE_PROCESSING;
lockfilePath: string;
dotPnpmFolderPath: string;
dotPnpmFolder: string;
}
| {
messageIdentifier: LogMessageIdentifier.PREPARE_REPLACING_FILE;
pnpmSyncJsonPath: string;
projectFolder: string;
actualVersion: string;
expectedVersion: string;
}
| {
messageIdentifier: LogMessageIdentifier.PREPARE_WRITING_FILE;
Expand All @@ -80,6 +90,12 @@ export type LogMessageDetails =
messageIdentifier: LogMessageIdentifier.COPY_ERROR_NO_SYNC_FILE;
pnpmSyncJsonPath: string;
}
| {
messageIdentifier: LogMessageIdentifier.COPY_ERROR_INCOMPATIBLE_SYNC_FILE;
pnpmSyncJsonPath: string;
actualVersion: string;
expectedVersion: string;
}
| {
messageIdentifier: LogMessageIdentifier.COPY_FINISHING;
pnpmSyncJsonPath: string;
Expand Down
35 changes: 30 additions & 5 deletions packages/pnpm-sync-lib/src/pnpmSyncCopy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import path from 'path';
import fs from 'fs';
import process from 'node:process';
import { ILogMessageCallbackOptions, LogMessageIdentifier, LogMessageKind } from './interfaces';
import {
ILogMessageCallbackOptions,
IPnpmSyncJson,
LogMessageIdentifier,
LogMessageKind
} from './interfaces';
import { pnpmSyncGetJsonVersion } from './utilities';

/**
* @beta
Expand Down Expand Up @@ -93,17 +99,36 @@ export async function pnpmSyncCopyAsync(options: IPnpmSyncCopyOptions): Promise<

const pnpmSyncJsonFolder = path.dirname(pnpmSyncJsonPath);

//read the .pnpm-sync.json
const pnpmSyncJson = JSON.parse(pnpmSyncJsonContents);
// read the .pnpm-sync.json
const pnpmSyncJson: IPnpmSyncJson = JSON.parse(pnpmSyncJsonContents);

// verify if the version is incompatible
const expectedPnpmSyncJsonVersion: string = pnpmSyncGetJsonVersion();
const actualPnpmSyncJsonVersion: string = pnpmSyncJson.version;
if (expectedPnpmSyncJsonVersion !== actualPnpmSyncJsonVersion) {
const errorMessage = `The .pnpm-sync.json file in ${pnpmSyncJsonFolder} has an incompatible version; regenerate it and try again.`;
logMessageCallback({
message: errorMessage,
messageKind: LogMessageKind.ERROR,
details: {
messageIdentifier: LogMessageIdentifier.COPY_ERROR_INCOMPATIBLE_SYNC_FILE,
pnpmSyncJsonPath,
actualVersion: actualPnpmSyncJsonVersion,
expectedVersion: expectedPnpmSyncJsonVersion
}
});
throw Error(errorMessage);
}

const { sourceFolder, targetFolders } = pnpmSyncJson.postbuildInjectedCopy;
const sourcePath = path.resolve(pnpmSyncJsonFolder, sourceFolder);

//get npmPackFiles
// get npmPackFiles
const npmPackFiles: string[] = await getPackageIncludedFiles(sourcePath);

const startTime = process.hrtime.bigint();

//clear the destination folder first
// clear the destination folder first
for (const targetFolder of targetFolders) {
const destinationPath = path.resolve(pnpmSyncJsonFolder, targetFolder.folderPath);
// TODO: optimize this
Expand Down
25 changes: 24 additions & 1 deletion packages/pnpm-sync-lib/src/pnpmSyncPrepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
IVersionSpecifier,
ILockfilePackage
} from './interfaces';
import { pnpmSyncGetJsonVersion } from './utilities';

/**
* @beta
Expand Down Expand Up @@ -163,7 +164,10 @@ export async function pnpmSyncPrepareAsync(options: IPnpmSyncPrepareOptions): Pr
await ensureFolder(pnpmSyncJsonFolder);
}

const expectedPnpmSyncJsonVersion: string = pnpmSyncGetJsonVersion();

let pnpmSyncJsonFile: IPnpmSyncJson = {
version: expectedPnpmSyncJsonVersion,
postbuildInjectedCopy: {
sourceFolder: '..', // path from pnpmSyncJsonFolder to projectFolder
targetFolders: []
Expand All @@ -172,7 +176,26 @@ export async function pnpmSyncPrepareAsync(options: IPnpmSyncPrepareOptions): Pr

// if .pnpm-sync.json already exists, read it first
if (fs.existsSync(pnpmSyncJsonPath)) {
pnpmSyncJsonFile = JSON.parse(fs.readFileSync(pnpmSyncJsonPath).toString());
const existingPnpmSyncJsonFile: IPnpmSyncJson = JSON.parse(
fs.readFileSync(pnpmSyncJsonPath).toString()
);

const actualPnpmSyncJsonVersion: string = existingPnpmSyncJsonFile?.version;
if (actualPnpmSyncJsonVersion === expectedPnpmSyncJsonVersion) {
pnpmSyncJsonFile = existingPnpmSyncJsonFile;
} else {
logMessageCallback({
message: `The .pnpm-sync.json file in ${pnpmSyncJsonFolder} has an incompatible version; pnpm-sync will regenerate it.`,
messageKind: LogMessageKind.VERBOSE,
details: {
messageIdentifier: LogMessageIdentifier.PREPARE_REPLACING_FILE,
pnpmSyncJsonPath,
projectFolder,
actualVersion: actualPnpmSyncJsonVersion,
expectedVersion: expectedPnpmSyncJsonVersion
}
});
}
}

const existingTargetFolderSet: Set<string> = new Set();
Expand Down
8 changes: 8 additions & 0 deletions packages/pnpm-sync-lib/src/utilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Get .pnpm-sync.json version
*
* @beta
*/
export function pnpmSyncGetJsonVersion(): string {
return require('../package.json').version;
}
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.0",
"version": "0.2.1",
"description": "Recopy injected dependencies whenever a project is rebuilt in your PNPM workspace",
"keywords": [
"rush",
Expand Down
116 changes: 116 additions & 0 deletions tests/pnpm-sync-api-test/src/test/pnpmSyncCopy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import fs from 'fs';
import path from 'path';
import { Async, FileSystem } from '@rushstack/node-core-library';
import { PackageExtractor } from '@rushstack/package-extractor';
import { readPnpmLockfile, scrubLog } from './testUtilities';
import {
ILogMessageCallbackOptions,
pnpmSyncGetJsonVersion,
pnpmSyncCopyAsync,
pnpmSyncPrepareAsync
} from 'pnpm-sync-lib';

const pnpmSyncLibVersion: string = pnpmSyncGetJsonVersion();

describe('pnpm-sync-api copy test', () => {
it('pnpmSyncCopyAsync should copy files based on .pnpm-sync.json under node_modules folder', async () => {
const lockfilePath = '../../pnpm-lock.yaml';
const dotPnpmFolder = '../../node_modules/.pnpm';

const pnpmSyncJsonFolder = `../test-fixtures/sample-lib1/node_modules`;
const pnpmSyncJsonPath = `${pnpmSyncJsonFolder}/.pnpm-sync.json`;
const targetFolderPath =
'../../../../node_modules/.pnpm/[email protected]/node_modules/api-demo-sample-lib1';
// generate .pnpm-sync.json file first.
await pnpmSyncPrepareAsync({
lockfilePath: lockfilePath,
dotPnpmFolder: dotPnpmFolder,
ensureFolder: FileSystem.ensureFolderAsync,
readPnpmLockfile,
logMessageCallback: (): void => {}
});

// make sure .pnpm-sync.json exists
expect(fs.existsSync(pnpmSyncJsonPath)).toBe(true);

const pnpmSyncJsonFile = JSON.parse(fs.readFileSync(pnpmSyncJsonPath).toString());
expect(pnpmSyncJsonFile).toEqual({
version: pnpmSyncLibVersion,
postbuildInjectedCopy: {
sourceFolder: '..',
targetFolders: [
{
folderPath: targetFolderPath
}
]
}
});

// set a outdated version
pnpmSyncJsonFile.version = 'incompatible-version';
await fs.promises.writeFile(pnpmSyncJsonPath, JSON.stringify(pnpmSyncJsonFile, null, 2));

// if pnpmSyncCopyAsync detects a incompatible version, should throw errors
try {
await pnpmSyncCopyAsync({
pnpmSyncJsonPath,
getPackageIncludedFiles: PackageExtractor.getPackageIncludedFilesAsync,
forEachAsyncWithConcurrency: Async.forEachAsync,
ensureFolder: FileSystem.ensureFolderAsync,
logMessageCallback: () => {}
});
} catch (error) {
expect(error.message).toContain('has an incompatible version; regenerate it and try again');
}

// set the correct version
pnpmSyncJsonFile.version = pnpmSyncLibVersion;
await fs.promises.writeFile(pnpmSyncJsonPath, JSON.stringify(pnpmSyncJsonFile, null, 2));

const destinationPath = path.resolve(pnpmSyncJsonFolder, targetFolderPath);

// delete the destination folder
await fs.promises.rm(destinationPath, { recursive: true, force: true });
expect(fs.existsSync(destinationPath)).toBe(false);

const logs: ILogMessageCallbackOptions[] = [];

await pnpmSyncCopyAsync({
pnpmSyncJsonPath,
getPackageIncludedFiles: PackageExtractor.getPackageIncludedFilesAsync,
forEachAsyncWithConcurrency: Async.forEachAsync,
ensureFolder: FileSystem.ensureFolderAsync,
logMessageCallback: (options: ILogMessageCallbackOptions): void => {
logs.push(options);
}
});

// after copy action, the destination folder should exists
expect(fs.existsSync(destinationPath)).toBe(true);

// check the log message
expect(logs.map((x) => scrubLog(x))).toMatchInlineSnapshot(`
Array [
Object {
"details": Object {
"messageIdentifier": "copy-starting",
"pnpmSyncJsonPath": "<root>/pnpm-sync/tests/test-fixtures/sample-lib1/node_modules/.pnpm-sync.json",
},
"message": "Starting...",
"messageKind": "verbose",
},
Object {
"details": Object {
"executionTimeInMs": "[TIMING]",
"fileCount": 6,
"messageIdentifier": "copy-finishing",
"pnpmSyncJsonPath": "<root>/pnpm-sync/tests/test-fixtures/sample-lib1/node_modules/.pnpm-sync.json",
"sourcePath": "<root>/pnpm-sync/tests/test-fixtures/sample-lib1",
},
"message": "Synced...",
"messageKind": "info",
},
]
`);
});
});
Loading
Loading