mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-10 12:57:47 +00:00
Updates package manager detection to account for two lock files. All other managers will only have the one lock file. Bun, however, may have both a `bun.lockb` _and_ a `yarn.lock` file. To ensure bun is properly detected, the presence of `bun.lockb` with `yarn.lock` must occur before `yarn.lock` so we don't mistake the presence of a `yarn.lock` to mean "Yarn". This PR also adds a test for this situation in `fs-detectors`. The behavior is currently correct there, but was not tested initially. It is now to avoid future regressions.
680 lines
23 KiB
TypeScript
Vendored
680 lines
23 KiB
TypeScript
Vendored
import ms from 'ms';
|
|
import path from 'path';
|
|
import fs from 'fs-extra';
|
|
import { strict as assert } from 'assert';
|
|
import { getSupportedNodeVersion } from '../src/fs/node-version';
|
|
import {
|
|
FileBlob,
|
|
getNodeVersion,
|
|
getLatestNodeVersion,
|
|
getDiscontinuedNodeVersions,
|
|
rename,
|
|
runNpmInstall,
|
|
runPackageJsonScript,
|
|
scanParentDirs,
|
|
Prerender,
|
|
} from '../src';
|
|
import type { Files } from '../src';
|
|
|
|
jest.setTimeout(10 * 1000);
|
|
|
|
async function expectBuilderError(promise: Promise<any>, pattern: string) {
|
|
let result;
|
|
try {
|
|
result = await promise;
|
|
} catch (error) {
|
|
result = error;
|
|
}
|
|
assert('message' in result, `Expected error message but found ${result}`);
|
|
assert(
|
|
typeof result.message === 'string',
|
|
`Expected error to be a string but found ${typeof result.message}`
|
|
);
|
|
assert(
|
|
result.message.includes(pattern),
|
|
`Expected ${pattern} but found "${result.message}"`
|
|
);
|
|
}
|
|
|
|
let warningMessages: string[];
|
|
const originalConsoleWarn = console.warn;
|
|
beforeEach(() => {
|
|
warningMessages = [];
|
|
console.warn = m => {
|
|
warningMessages.push(m);
|
|
};
|
|
});
|
|
|
|
afterEach(() => {
|
|
console.warn = originalConsoleWarn;
|
|
});
|
|
|
|
it('should only match supported node versions, otherwise throw an error', async () => {
|
|
expect(await getSupportedNodeVersion('16.x', false)).toHaveProperty(
|
|
'major',
|
|
16
|
|
);
|
|
expect(await getSupportedNodeVersion('18.x', false)).toHaveProperty(
|
|
'major',
|
|
18
|
|
);
|
|
|
|
const autoMessage =
|
|
'Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.';
|
|
await expectBuilderError(
|
|
getSupportedNodeVersion('8.11.x', true),
|
|
autoMessage
|
|
);
|
|
await expectBuilderError(getSupportedNodeVersion('6.x', true), autoMessage);
|
|
await expectBuilderError(getSupportedNodeVersion('999.x', true), autoMessage);
|
|
await expectBuilderError(getSupportedNodeVersion('foo', true), autoMessage);
|
|
await expectBuilderError(getSupportedNodeVersion('=> 10', true), autoMessage);
|
|
|
|
expect(await getSupportedNodeVersion('16.x', true)).toHaveProperty(
|
|
'major',
|
|
16
|
|
);
|
|
expect(await getSupportedNodeVersion('18.x', true)).toHaveProperty(
|
|
'major',
|
|
18
|
|
);
|
|
|
|
const foundMessage =
|
|
'Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.';
|
|
await expectBuilderError(
|
|
getSupportedNodeVersion('8.11.x', false),
|
|
foundMessage
|
|
);
|
|
await expectBuilderError(getSupportedNodeVersion('6.x', false), foundMessage);
|
|
await expectBuilderError(
|
|
getSupportedNodeVersion('999.x', false),
|
|
foundMessage
|
|
);
|
|
await expectBuilderError(getSupportedNodeVersion('foo', false), foundMessage);
|
|
await expectBuilderError(
|
|
getSupportedNodeVersion('=> 10', false),
|
|
foundMessage
|
|
);
|
|
});
|
|
|
|
it('should match all semver ranges', async () => {
|
|
// See https://docs.npmjs.com/files/package.json#engines
|
|
expect(await getSupportedNodeVersion('16.0.0')).toHaveProperty('major', 16);
|
|
expect(await getSupportedNodeVersion('16.x')).toHaveProperty('major', 16);
|
|
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 18);
|
|
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 18);
|
|
expect(await getSupportedNodeVersion('16.5.0 - 16.9.0')).toHaveProperty(
|
|
'major',
|
|
16
|
|
);
|
|
expect(await getSupportedNodeVersion('>=9.5.0 <=16.5.0')).toHaveProperty(
|
|
'major',
|
|
16
|
|
);
|
|
expect(await getSupportedNodeVersion('~16.5.0')).toHaveProperty('major', 16);
|
|
expect(await getSupportedNodeVersion('^16.5.0')).toHaveProperty('major', 16);
|
|
expect(await getSupportedNodeVersion('16.5.0 - 16.20.0')).toHaveProperty(
|
|
'major',
|
|
16
|
|
);
|
|
});
|
|
|
|
it('should allow nodejs18.x', async () => {
|
|
expect(getLatestNodeVersion()).toHaveProperty('major', 18);
|
|
expect(await getSupportedNodeVersion('18.x')).toHaveProperty('major', 18);
|
|
expect(await getSupportedNodeVersion('18')).toHaveProperty('major', 18);
|
|
expect(await getSupportedNodeVersion('18.1.0')).toHaveProperty('major', 18);
|
|
expect(await getSupportedNodeVersion('>=16')).toHaveProperty('major', 18);
|
|
});
|
|
|
|
it('should ignore node version in vercel dev getNodeVersion()', async () => {
|
|
expect(
|
|
await getNodeVersion(
|
|
'/tmp',
|
|
undefined,
|
|
{ nodeVersion: '1' },
|
|
{ isDev: true }
|
|
)
|
|
).toHaveProperty('runtime', 'nodejs');
|
|
});
|
|
|
|
it('should select project setting from config when no package.json is found and fallback undefined', async () => {
|
|
expect(
|
|
await getNodeVersion('/tmp', undefined, { nodeVersion: '18.x' }, {})
|
|
).toHaveProperty('range', '18.x');
|
|
expect(warningMessages).toStrictEqual([]);
|
|
});
|
|
|
|
it('should select project setting from config when no package.json is found and fallback is null', async () => {
|
|
expect(
|
|
await getNodeVersion('/tmp', null as any, { nodeVersion: '18.x' }, {})
|
|
).toHaveProperty('range', '18.x');
|
|
expect(warningMessages).toStrictEqual([]);
|
|
});
|
|
|
|
it('should select project setting from fallback when no package.json is found', async () => {
|
|
expect(await getNodeVersion('/tmp', '18.x')).toHaveProperty('range', '18.x');
|
|
expect(warningMessages).toStrictEqual([]);
|
|
});
|
|
|
|
it('should prefer package.json engines over project setting from config and warn', async () => {
|
|
expect(
|
|
await getNodeVersion(
|
|
path.join(__dirname, 'pkg-engine-node'),
|
|
undefined,
|
|
{ nodeVersion: '12.x' },
|
|
{}
|
|
)
|
|
).toHaveProperty('range', '18.x');
|
|
expect(warningMessages).toStrictEqual([
|
|
'Warning: Due to "engines": { "node": "18.x" } in your `package.json` file, the Node.js Version defined in your Project Settings ("12.x") will not apply. Learn More: http://vercel.link/node-version',
|
|
]);
|
|
});
|
|
|
|
it('should warn when package.json engines is exact version', async () => {
|
|
expect(
|
|
await getNodeVersion(
|
|
path.join(__dirname, 'pkg-engine-node-exact'),
|
|
undefined,
|
|
{},
|
|
{}
|
|
)
|
|
).toHaveProperty('range', '18.x');
|
|
expect(warningMessages).toStrictEqual([
|
|
'Warning: Detected "engines": { "node": "18.2.0" } in your `package.json` with major.minor.patch, but only major Node.js Version can be selected. Learn More: http://vercel.link/node-version',
|
|
]);
|
|
});
|
|
|
|
it('should warn when package.json engines is greater than', async () => {
|
|
expect(
|
|
await getNodeVersion(
|
|
path.join(__dirname, 'pkg-engine-node-greaterthan'),
|
|
undefined,
|
|
{},
|
|
{}
|
|
)
|
|
).toHaveProperty('range', '18.x');
|
|
expect(warningMessages).toStrictEqual([
|
|
'Warning: Detected "engines": { "node": ">=16" } in your `package.json` that will automatically upgrade when a new major Node.js Version is released. Learn More: http://vercel.link/node-version',
|
|
]);
|
|
});
|
|
|
|
it('should not warn when package.json engines matches project setting from config', async () => {
|
|
expect(
|
|
await getNodeVersion(
|
|
path.join(__dirname, 'pkg-engine-node'),
|
|
undefined,
|
|
{ nodeVersion: '18' },
|
|
{}
|
|
)
|
|
).toHaveProperty('range', '18.x');
|
|
expect(warningMessages).toStrictEqual([]);
|
|
|
|
expect(
|
|
await getNodeVersion(
|
|
path.join(__dirname, 'pkg-engine-node'),
|
|
undefined,
|
|
{ nodeVersion: '18.x' },
|
|
{}
|
|
)
|
|
).toHaveProperty('range', '18.x');
|
|
expect(warningMessages).toStrictEqual([]);
|
|
|
|
expect(
|
|
await getNodeVersion(
|
|
path.join(__dirname, 'pkg-engine-node'),
|
|
undefined,
|
|
{ nodeVersion: '<19' },
|
|
{}
|
|
)
|
|
).toHaveProperty('range', '18.x');
|
|
expect(warningMessages).toStrictEqual([]);
|
|
});
|
|
|
|
it('should get latest node version', async () => {
|
|
expect(getLatestNodeVersion()).toHaveProperty('major', 18);
|
|
});
|
|
|
|
it('should throw for discontinued versions', async () => {
|
|
// Mock a future date so that Node 8 and 10 become discontinued
|
|
const realDateNow = Date.now.bind(global.Date);
|
|
global.Date.now = () => new Date('2024-02-13').getTime();
|
|
|
|
expect(getSupportedNodeVersion('8.10.x', false)).rejects.toThrow();
|
|
expect(getSupportedNodeVersion('8.10.x', true)).rejects.toThrow();
|
|
expect(getSupportedNodeVersion('10.x', false)).rejects.toThrow();
|
|
expect(getSupportedNodeVersion('10.x', true)).rejects.toThrow();
|
|
expect(getSupportedNodeVersion('12.x', false)).rejects.toThrow();
|
|
expect(getSupportedNodeVersion('12.x', true)).rejects.toThrow();
|
|
expect(getSupportedNodeVersion('14.x', false)).rejects.toThrow();
|
|
expect(getSupportedNodeVersion('14.x', true)).rejects.toThrow();
|
|
expect(getSupportedNodeVersion('16.x', false)).rejects.toThrow();
|
|
expect(getSupportedNodeVersion('16.x', true)).rejects.toThrow();
|
|
|
|
const discontinued = getDiscontinuedNodeVersions();
|
|
expect(discontinued.length).toBe(5);
|
|
expect(discontinued[0]).toHaveProperty('range', '16.x');
|
|
expect(discontinued[1]).toHaveProperty('range', '14.x');
|
|
expect(discontinued[2]).toHaveProperty('range', '12.x');
|
|
expect(discontinued[3]).toHaveProperty('range', '10.x');
|
|
expect(discontinued[4]).toHaveProperty('range', '8.10.x');
|
|
|
|
global.Date.now = realDateNow;
|
|
});
|
|
|
|
it('should warn for deprecated versions, soon to be discontinued', async () => {
|
|
// Mock a future date so that Node 10 warns
|
|
const realDateNow = Date.now.bind(global.Date);
|
|
global.Date.now = () => new Date('2021-02-23').getTime();
|
|
|
|
expect(await getSupportedNodeVersion('10.x', false)).toHaveProperty(
|
|
'major',
|
|
10
|
|
);
|
|
expect(await getSupportedNodeVersion('10.x', true)).toHaveProperty(
|
|
'major',
|
|
10
|
|
);
|
|
expect(await getSupportedNodeVersion('12.x', false)).toHaveProperty(
|
|
'major',
|
|
12
|
|
);
|
|
expect(await getSupportedNodeVersion('12.x', true)).toHaveProperty(
|
|
'major',
|
|
12
|
|
);
|
|
expect(await getSupportedNodeVersion('14.x', false)).toHaveProperty(
|
|
'major',
|
|
14
|
|
);
|
|
expect(await getSupportedNodeVersion('14.x', true)).toHaveProperty(
|
|
'major',
|
|
14
|
|
);
|
|
expect(await getSupportedNodeVersion('16.x', false)).toHaveProperty(
|
|
'major',
|
|
16
|
|
);
|
|
expect(await getSupportedNodeVersion('16.x', true)).toHaveProperty(
|
|
'major',
|
|
16
|
|
);
|
|
expect(warningMessages).toStrictEqual([
|
|
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
|
|
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
|
|
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
|
|
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
|
|
'Error: Node.js version 14.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
|
|
'Error: Node.js version 14.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
|
|
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2024-02-06 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
|
|
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2024-02-06 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
|
|
]);
|
|
|
|
global.Date.now = realDateNow;
|
|
});
|
|
|
|
it('should support initialHeaders and initialStatus correctly', async () => {
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
initialHeaders: {
|
|
'content-type': 'application/json',
|
|
'x-initial': 'true',
|
|
},
|
|
initialStatus: 308,
|
|
});
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
initialStatus: 308,
|
|
});
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
initialHeaders: {
|
|
'content-type': 'application/json',
|
|
'x-initial': 'true',
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should support experimentalBypassFor correctly', async () => {
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
experimentalBypassFor: [{ type: 'header', key: 'Next-Action' }],
|
|
});
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
experimentalBypassFor: [
|
|
{ type: 'header', key: 'Next-Action' },
|
|
{
|
|
type: 'cookie',
|
|
key: '__prerender_bypass',
|
|
value: 'some-long-bypass-token-to-make-it-work',
|
|
},
|
|
],
|
|
});
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
experimentalBypassFor: [{ type: 'query', key: 'bypass', value: '1' }],
|
|
});
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
experimentalBypassFor: [{ type: 'host', value: 'vercel.com' }],
|
|
});
|
|
|
|
expect(() => {
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
// @ts-expect-error: testing invalid args
|
|
experimentalBypassFor: 'foo',
|
|
});
|
|
}).toThrowError(
|
|
'The `experimentalBypassFor` argument for `Prerender` must be Array of objects with fields `type`, `key` and optionally `value`.'
|
|
);
|
|
|
|
expect(() => {
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
// @ts-expect-error: testing invalid args
|
|
experimentalBypassFor: [{ type: 'header', value: { foo: 'bar' } }],
|
|
});
|
|
}).toThrowError(
|
|
'The `experimentalBypassFor` argument for `Prerender` must be Array of objects with fields `type`, `key` and optionally `value`.'
|
|
);
|
|
});
|
|
|
|
it('should support passQuery correctly', async () => {
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
passQuery: true,
|
|
});
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
passQuery: false,
|
|
});
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
passQuery: undefined,
|
|
});
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
});
|
|
|
|
expect(() => {
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
// @ts-expect-error testing invalid field
|
|
passQuery: 'true',
|
|
});
|
|
}).toThrowError(
|
|
`The \`passQuery\` argument for \`Prerender\` must be a boolean.`
|
|
);
|
|
});
|
|
|
|
it('should support experimentalStreamingLambdaPath correctly', async () => {
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
experimentalStreamingLambdaPath: undefined,
|
|
});
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
experimentalStreamingLambdaPath: '/some/path/to/lambda',
|
|
});
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
});
|
|
|
|
expect(() => {
|
|
new Prerender({
|
|
expiration: 1,
|
|
fallback: null,
|
|
group: 1,
|
|
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
|
// @ts-expect-error testing invalid field
|
|
experimentalStreamingLambdaPath: 1,
|
|
});
|
|
}).toThrowError(
|
|
`The \`experimentalStreamingLambdaPath\` argument for \`Prerender\` must be a string.`
|
|
);
|
|
});
|
|
|
|
it('should support require by path for legacy builders', () => {
|
|
const index = require('../');
|
|
|
|
const download2 = require('../fs/download.js');
|
|
const getWriteableDirectory2 = require('../fs/get-writable-directory.js');
|
|
const glob2 = require('../fs/glob.js');
|
|
const rename2 = require('../fs/rename.js');
|
|
const {
|
|
runNpmInstall: runNpmInstall2,
|
|
} = require('../fs/run-user-scripts.js');
|
|
const streamToBuffer2 = require('../fs/stream-to-buffer.js');
|
|
|
|
const FileBlob2 = require('../file-blob.js');
|
|
const FileFsRef2 = require('../file-fs-ref.js');
|
|
const FileRef2 = require('../file-ref.js');
|
|
const { Lambda: Lambda2 } = require('../lambda.js');
|
|
|
|
expect(download2).toBe(index.download);
|
|
expect(getWriteableDirectory2).toBe(index.getWriteableDirectory);
|
|
expect(glob2).toBe(index.glob);
|
|
expect(rename2).toBe(index.rename);
|
|
expect(runNpmInstall2).toBe(index.runNpmInstall);
|
|
expect(streamToBuffer2).toBe(index.streamToBuffer);
|
|
|
|
expect(FileBlob2).toBe(index.FileBlob);
|
|
expect(FileFsRef2).toBe(index.FileFsRef);
|
|
expect(FileRef2).toBe(index.FileRef);
|
|
expect(Lambda2).toBe(index.Lambda);
|
|
});
|
|
|
|
it(
|
|
'should have correct $PATH when running `runPackageJsonScript()` with yarn',
|
|
async () => {
|
|
if (process.platform === 'win32') {
|
|
console.log('Skipping test on windows');
|
|
return;
|
|
}
|
|
if (process.platform === 'darwin') {
|
|
console.log('Skipping test on macOS');
|
|
return;
|
|
}
|
|
const fixture = path.join(__dirname, 'fixtures', '19-yarn-v2');
|
|
await runNpmInstall(fixture);
|
|
await runPackageJsonScript(fixture, 'env');
|
|
|
|
// `yarn` was failing with ENOENT before, so as long as the
|
|
// script was invoked at all is enough to verify the fix
|
|
const out = await fs.readFile(path.join(fixture, 'env.txt'), 'utf8');
|
|
expect(out.trim()).toBeTruthy();
|
|
},
|
|
ms('1m')
|
|
);
|
|
|
|
it('should return cliType bun and correct lock file for bun v1', async () => {
|
|
const fixture = path.join(__dirname, 'fixtures', '31-bun-v1-with-yarn-lock');
|
|
const result = await scanParentDirs(fixture);
|
|
expect(result.cliType).toEqual('bun');
|
|
expect(result.lockfileVersion).toEqual(0);
|
|
expect(result.lockfilePath).toEqual(path.join(fixture, 'bun.lockb'));
|
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
|
});
|
|
|
|
it('should return cliType bun and correct lock file for bun v1 with yarn.lock file', async () => {
|
|
const fixture = path.join(__dirname, 'fixtures', '30-bun-v1');
|
|
const result = await scanParentDirs(fixture);
|
|
expect(result.cliType).toEqual('bun');
|
|
expect(result.lockfileVersion).toEqual(0);
|
|
expect(result.lockfilePath).toEqual(path.join(fixture, 'bun.lockb'));
|
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
|
});
|
|
|
|
it('should return lockfileVersion 2 with npm7', async () => {
|
|
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
|
|
const result = await scanParentDirs(fixture);
|
|
expect(result.cliType).toEqual('npm');
|
|
expect(result.lockfileVersion).toEqual(2);
|
|
expect(result.lockfilePath).toEqual(path.join(fixture, 'package-lock.json'));
|
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
|
});
|
|
|
|
it('should not return lockfileVersion with yarn', async () => {
|
|
const fixture = path.join(__dirname, 'fixtures', '19-yarn-v2');
|
|
const result = await scanParentDirs(fixture);
|
|
expect(result.cliType).toEqual('yarn');
|
|
expect(result.lockfileVersion).toEqual(undefined);
|
|
expect(result.lockfilePath).toEqual(path.join(fixture, 'yarn.lock'));
|
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
|
});
|
|
|
|
it('should return lockfileVersion 1 with older versions of npm', async () => {
|
|
const fixture = path.join(__dirname, 'fixtures', '08-yarn-npm/with-npm');
|
|
const result = await scanParentDirs(fixture);
|
|
expect(result.cliType).toEqual('npm');
|
|
expect(result.lockfileVersion).toEqual(1);
|
|
expect(result.lockfilePath).toEqual(path.join(fixture, 'package-lock.json'));
|
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
|
});
|
|
|
|
it('should detect npm Workspaces', async () => {
|
|
const fixture = path.join(__dirname, 'fixtures', '21-npm-workspaces/a');
|
|
const result = await scanParentDirs(fixture);
|
|
expect(result.cliType).toEqual('npm');
|
|
expect(result.lockfileVersion).toEqual(2);
|
|
expect(result.lockfilePath).toEqual(
|
|
path.join(fixture, '..', 'package-lock.json')
|
|
);
|
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
|
});
|
|
|
|
it('should detect pnpm without workspace', async () => {
|
|
const fixture = path.join(__dirname, 'fixtures', '22-pnpm');
|
|
const result = await scanParentDirs(fixture);
|
|
expect(result.cliType).toEqual('pnpm');
|
|
expect(result.lockfileVersion).toEqual(5.3);
|
|
expect(result.lockfilePath).toEqual(path.join(fixture, 'pnpm-lock.yaml'));
|
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
|
});
|
|
|
|
it('should detect pnpm with workspaces', async () => {
|
|
const fixture = path.join(__dirname, 'fixtures', '23-pnpm-workspaces/c');
|
|
const result = await scanParentDirs(fixture);
|
|
expect(result.cliType).toEqual('pnpm');
|
|
expect(result.lockfileVersion).toEqual(5.3);
|
|
expect(result.lockfilePath).toEqual(
|
|
path.join(fixture, '..', 'pnpm-lock.yaml')
|
|
);
|
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
|
});
|
|
|
|
it('should detect package.json in nested backend', async () => {
|
|
const fixture = path.join(
|
|
__dirname,
|
|
'../../node/test/fixtures/18.1-nested-packagejson/backend'
|
|
);
|
|
const result = await scanParentDirs(fixture);
|
|
expect(result.cliType).toEqual('pnpm');
|
|
// There is no lockfile but this test will pick up vercel/vercel/pnpm-lock.yaml
|
|
expect(result.lockfileVersion).toEqual(6);
|
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
|
});
|
|
|
|
it('should detect package.json in nested frontend', async () => {
|
|
const fixture = path.join(
|
|
__dirname,
|
|
'../../node/test/fixtures/18.1-nested-packagejson/frontend'
|
|
);
|
|
const result = await scanParentDirs(fixture);
|
|
expect(result.cliType).toEqual('pnpm');
|
|
// There is no lockfile but this test will pick up vercel/vercel/pnpm-lock.yaml
|
|
expect(result.lockfileVersion).toEqual(6);
|
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
|
});
|
|
|
|
it('should retry npm install when peer deps invalid and npm@8 on node@16', async () => {
|
|
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
|
if (nodeMajor !== 16) {
|
|
console.log(`Skipping test on node@${nodeMajor}`);
|
|
return;
|
|
}
|
|
if (process.platform === 'win32') {
|
|
console.log('Skipping test on windows');
|
|
return;
|
|
}
|
|
if (process.platform === 'darwin') {
|
|
console.log('Skipping test on mac');
|
|
return;
|
|
}
|
|
|
|
const fixture = path.join(__dirname, 'fixtures', '15-npm-8-legacy-peer-deps');
|
|
const nodeVersion = { major: nodeMajor } as any;
|
|
await runNpmInstall(fixture, [], {}, {}, nodeVersion);
|
|
expect(warningMessages).toStrictEqual([
|
|
'Warning: Retrying "Install Command" with `--legacy-peer-deps` which may accept a potentially broken dependency and slow install time.',
|
|
]);
|
|
});
|
|
|
|
describe('rename', () => {
|
|
it('should rename keys of files map', () => {
|
|
const before: Files = {};
|
|
const toUpper = (s: string) => s.toUpperCase();
|
|
|
|
for (let i = 97; i <= 122; i++) {
|
|
const key = String.fromCharCode(i);
|
|
before[key] = new FileBlob({ contentType: 'text/plain', data: key });
|
|
}
|
|
|
|
const after = rename(before, toUpper);
|
|
expect(Object.keys(after)).toEqual('ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''));
|
|
});
|
|
});
|