Skip to content

Commit

Permalink
update how and when we read lock file
Browse files Browse the repository at this point in the history
  • Loading branch information
rtpascual committed Jan 8, 2025
1 parent aa28d24 commit 8f615b2
Show file tree
Hide file tree
Showing 15 changed files with 154 additions and 275 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void describe('LockFileReaderFactory', () => {
});
}

void it('should throw an error for unsupport package managers', () => {
void it('should throw an error for unsupported package managers', () => {
process.env.npm_config_user_agent =
'unsupported/1.0.0 node/v15.0.0 darwin x64';
assert.throws(() => new LockFileReaderFactory().getLockFileReader(), {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import path from 'path';
import { NpmLockFileReader } from './npm_lock_file_reader';

void describe('NpmLockFileReader', () => {
const fspAccessMock = mock.method(fsp, 'access', () => true);
const fspReadFileMock = mock.method(fsp, 'readFile', () =>
JSON.stringify({
name: 'test_project',
Expand Down Expand Up @@ -47,37 +46,18 @@ void describe('NpmLockFileReader', () => {
};
assert.deepEqual(lockFileContents, expectedLockFileContents);
assert.strictEqual(
fspAccessMock.mock.calls[0].arguments[0],
fspReadFileMock.mock.calls[0].arguments[0],
path.resolve(process.cwd(), 'package-lock.json')
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
});

void it('throws when package-lock.json is not present', async () => {
fspAccessMock.mock.mockImplementationOnce(() =>
void it('returns empty lock file contents when package-lock.json is not present or parse-able', async () => {
fspReadFileMock.mock.mockImplementationOnce(() =>
Promise.reject(new Error())
);
await assert.rejects(
() => npmLockFileReader.getLockFileContentsFromCwd(),
(error: Error) => {
assert.ok(
error.message.startsWith('Could not find a package-lock.json file')
);
return true;
}
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 0);
});

void it('throws when package-lock.json is not parse-able', async () => {
fspReadFileMock.mock.mockImplementationOnce(() => 'not json content');
await assert.rejects(
() => npmLockFileReader.getLockFileContentsFromCwd(),
(error: Error) => {
assert.ok(error.message.startsWith('Could not parse the contents'));
return true;
}
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
const lockFileContents =
await npmLockFileReader.getLockFileContentsFromCwd();
assert.deepEqual(lockFileContents, { dependencies: [] });
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import fsp from 'fs/promises';
import path from 'path';
import z from 'zod';
import { AmplifyUserError } from '../errors';
import {
Dependencies,
LockFileContents,
Expand All @@ -13,40 +12,24 @@ import {
*/
export class NpmLockFileReader implements LockFileReader {
getLockFileContentsFromCwd = async (): Promise<LockFileContents> => {
const dependencies: Dependencies = [];
const packageLockJsonPath = path.resolve(
process.cwd(),
'package-lock.json'
);

try {
await fsp.access(packageLockJsonPath);
} catch (error) {
throw new AmplifyUserError('InvalidPackageLockJsonError', {
message: `Could not find a package-lock.json file at ${packageLockJsonPath}`,
resolution: `Ensure that ${packageLockJsonPath} exists and is a valid JSON file`,
});
}

let jsonLockParsedValue: Record<string, unknown>;
try {
const jsonLockContents = await fsp.readFile(packageLockJsonPath, 'utf-8');
jsonLockParsedValue = JSON.parse(jsonLockContents);
} catch (error) {
throw new AmplifyUserError(
'InvalidPackageLockJsonError',
{
message: `Could not parse the contents of ${packageLockJsonPath}`,
resolution: `Ensure that ${packageLockJsonPath} exists and is a valid JSON file`,
},
error as Error
);
// We failed to get lock file contents either because file doesn't exist or it is not parse-able
return { dependencies };
}

// This will strip fields that are not part of the package lock schema
const packageLockJson = packageLockJsonSchema.parse(jsonLockParsedValue);

const dependencies: Dependencies = [];

for (const key in packageLockJson.packages) {
if (key === '') {
// Skip root project in packages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import path from 'path';
import { PnpmLockFileReader } from './pnpm_lock_file_reader';

void describe('PnpmLockFileReader', () => {
const fspAccessMock = mock.method(fsp, 'access', () => true);
const fspReadFileMock = mock.method(
fsp,
'readFile',
// eslint-disable-next-line spellcheck/spell-checker
() => `lockfileVersion: '9.0'
settings:
Expand Down Expand Up @@ -81,39 +81,18 @@ packages:
};
assert.deepEqual(lockFileContents, expectedLockFileContents);
assert.strictEqual(
fspAccessMock.mock.calls[0].arguments[0],
fspReadFileMock.mock.calls[0].arguments[0],
path.resolve(process.cwd(), 'pnpm-lock.yaml')
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
});

void it('throws when pnpm-lock.yaml is not present', async () => {
fspAccessMock.mock.mockImplementationOnce(() =>
Promise.reject(new Error())
);
await assert.rejects(
() => pnpmLockFileReader.getLockFileContentsFromCwd(),
(error: Error) => {
assert.ok(
error.message.startsWith('Could not find a pnpm-lock.yaml file')
);
return true;
}
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 0);
});

void it('throws when pnpm-lock.yaml is not parse-able', async () => {
void it('returns empty lock file contents when pnpm-lock.yaml is not present or parse-able', async () => {
fspReadFileMock.mock.mockImplementationOnce(() =>
Promise.reject(new Error())
);
await assert.rejects(
() => pnpmLockFileReader.getLockFileContentsFromCwd(),
(error: Error) => {
assert.ok(error.message.startsWith('Could not parse the contents'));
return true;
}
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
const lockFileContents =
await pnpmLockFileReader.getLockFileContentsFromCwd();
assert.deepEqual(lockFileContents, { dependencies: [] });
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,14 @@ import {
LockFileContents,
LockFileReader,
} from './lock_file_reader_factory';
import { AmplifyUserError } from '../errors';

/**
* PnpmLockFileReader is an abstraction around the logic used to read and parse lock file contents
*/
export class PnpmLockFileReader implements LockFileReader {
getLockFileContentsFromCwd = async (): Promise<LockFileContents> => {
const pnpmLockPath = path.resolve(process.cwd(), 'pnpm-lock.yaml');

try {
await fsp.access(pnpmLockPath);
} catch (error) {
throw new AmplifyUserError('InvalidPackageLockJsonError', {
message: `Could not find a pnpm-lock.yaml file at ${pnpmLockPath}`,
resolution: `Ensure that ${pnpmLockPath} exists and is a valid pnpm-lock.yaml file`,
});
}

const dependencies: Dependencies = [];
const pnpmLockPath = path.resolve(process.cwd(), 'pnpm-lock.yaml');

try {
const pnpmLockContents = await fsp.readFile(pnpmLockPath, 'utf-8');
Expand Down Expand Up @@ -52,14 +41,8 @@ export class PnpmLockFileReader implements LockFileReader {
dependencies.push({ name: dependencyName, version: dependencyVersion });
}
} catch (error) {
throw new AmplifyUserError(
'InvalidPackageLockJsonError',
{
message: `Could not parse the contents of ${pnpmLockPath}`,
resolution: `Ensure that ${pnpmLockPath} exists and is a valid pnpm-lock.yaml file`,
},
error as Error
);
// We failed to get lock file contents either because file doesn't exist or it is not parse-able
return { dependencies };
}

return { dependencies };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import path from 'path';
import { YarnClassicLockFileReader } from './yarn_classic_lock_file_reader';

void describe('YarnClassicLockFileReader', () => {
const fspAccessMock = mock.method(fsp, 'access', () => true);
const fspReadFileMock = mock.method(
fsp,
'readFile',
Expand Down Expand Up @@ -51,37 +50,18 @@ [email protected]:
};
assert.deepEqual(lockFileContents, expectedLockFileContents);
assert.strictEqual(
fspAccessMock.mock.calls[0].arguments[0],
fspReadFileMock.mock.calls[0].arguments[0],
path.resolve(process.cwd(), 'yarn.lock')
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
});

void it('throws when yarn.lock is not present', async () => {
fspAccessMock.mock.mockImplementationOnce(() =>
Promise.reject(new Error())
);
await assert.rejects(
() => yarnClassicLockFileReader.getLockFileContentsFromCwd(),
(error: Error) => {
assert.ok(error.message.startsWith('Could not find a yarn.lock file'));
return true;
}
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 0);
});

void it('throws when yarn.lock is not parse-able', async () => {
void it('returns empty lock file contents when yarn.lock is not present or parse-able', async () => {
fspReadFileMock.mock.mockImplementationOnce(() =>
Promise.reject(new Error())
);
await assert.rejects(
() => yarnClassicLockFileReader.getLockFileContentsFromCwd(),
(error: Error) => {
assert.ok(error.message.startsWith('Could not parse the contents'));
return true;
}
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
const lockFileContents =
await yarnClassicLockFileReader.getLockFileContentsFromCwd();
assert.deepEqual(lockFileContents, { dependencies: [] });
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,14 @@ import {
LockFileContents,
LockFileReader,
} from './lock_file_reader_factory';
import { AmplifyUserError } from '../errors';

/**
* YarnClassicLockFileReader is an abstraction around the logic used to read and parse lock file contents
*/
export class YarnClassicLockFileReader implements LockFileReader {
getLockFileContentsFromCwd = async (): Promise<LockFileContents> => {
const yarnLockPath = path.resolve(process.cwd(), 'yarn.lock');

try {
await fsp.access(yarnLockPath);
} catch (error) {
throw new AmplifyUserError('InvalidPackageLockJsonError', {
message: `Could not find a yarn.lock file at ${yarnLockPath}`,
resolution: `Ensure that ${yarnLockPath} exists and is a valid yarn.lock file`,
});
}

const dependencies: Dependencies = [];
const yarnLockPath = path.resolve(process.cwd(), 'yarn.lock');

try {
const yarnLockContents = await fsp.readFile(yarnLockPath, 'utf-8');
Expand All @@ -46,14 +35,8 @@ export class YarnClassicLockFileReader implements LockFileReader {
dependencies.push({ name: dependencyName, version: dependencyVersion });
}
} catch (error) {
throw new AmplifyUserError(
'InvalidPackageLockJsonError',
{
message: `Could not parse the contents of ${yarnLockPath}`,
resolution: `Ensure that ${yarnLockPath} exists and is a valid yarn.lock file`,
},
error as Error
);
// We failed to get lock file contents either because file doesn't exist or it is not parse-able
return { dependencies };
}

return { dependencies };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import path from 'path';
import { YarnModernLockFileReader } from './yarn_modern_lock_file_reader';

void describe('YarnModernLockFileReader', () => {
const fspAccessMock = mock.method(fsp, 'access', () => true);
const fspReadFileMock = mock.method(
fsp,
'readFile',
Expand Down Expand Up @@ -58,37 +57,18 @@ __metadata:
};
assert.deepEqual(lockFileContents, expectedLockFileContents);
assert.strictEqual(
fspAccessMock.mock.calls[0].arguments[0],
fspReadFileMock.mock.calls[0].arguments[0],
path.resolve(process.cwd(), 'yarn.lock')
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
});

void it('throws when yarn.lock is not present', async () => {
fspAccessMock.mock.mockImplementationOnce(() =>
Promise.reject(new Error())
);
await assert.rejects(
() => yarnModernLockFileReader.getLockFileContentsFromCwd(),
(error: Error) => {
assert.ok(error.message.startsWith('Could not find a yarn.lock file'));
return true;
}
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 0);
});

void it('throws when yarn.lock is not parse-able', async () => {
void it('returns empty lock file contents when yarn.lock is not present or parse-able', async () => {
fspReadFileMock.mock.mockImplementationOnce(() =>
Promise.reject(new Error())
);
await assert.rejects(
() => yarnModernLockFileReader.getLockFileContentsFromCwd(),
(error: Error) => {
assert.ok(error.message.startsWith('Could not parse the contents'));
return true;
}
);
assert.strictEqual(fspReadFileMock.mock.callCount(), 1);
const lockFileContents =
await yarnModernLockFileReader.getLockFileContentsFromCwd();
assert.deepEqual(lockFileContents, { dependencies: [] });
});
});
Loading

0 comments on commit 8f615b2

Please sign in to comment.