mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-07 21:07:46 +00:00
992 lines
32 KiB
JavaScript
992 lines
32 KiB
JavaScript
process.env.NEXT_TELEMETRY_DISABLED = '1';
|
|
|
|
const path = require('path');
|
|
const fs = require('fs-extra');
|
|
const builder = require('../../');
|
|
const {
|
|
createRunBuildLambda,
|
|
} = require('../../../../test/lib/run-build-lambda');
|
|
|
|
const runBuildLambda = createRunBuildLambda(builder);
|
|
|
|
jest.setTimeout(360000);
|
|
|
|
// experimental appDir currently requires Node.js >= 16
|
|
if (parseInt(process.versions.node.split('.')[0], 10) >= 16) {
|
|
it('should build with app-dir correctly', async () => {
|
|
const { buildResult } = await runBuildLambda(
|
|
path.join(__dirname, '../fixtures/00-app-dir')
|
|
);
|
|
|
|
const lambdas = new Set();
|
|
|
|
for (const key of Object.keys(buildResult.output)) {
|
|
if (buildResult.output[key].type === 'Lambda') {
|
|
lambdas.add(buildResult.output[key]);
|
|
}
|
|
}
|
|
|
|
expect(lambdas.size).toBe(1);
|
|
expect(buildResult.output['dashboard']).toBeDefined();
|
|
expect(buildResult.output['dashboard/another']).toBeDefined();
|
|
expect(buildResult.output['dashboard/changelog']).toBeDefined();
|
|
expect(buildResult.output['dashboard/deployments/[id]']).toBeDefined();
|
|
|
|
// prefixed static generation output with `/app` under dist server files
|
|
expect(buildResult.output['dashboard'].type).toBe('Prerender');
|
|
expect(buildResult.output['dashboard'].fallback.fsPath).toMatch(
|
|
/server\/app\/dashboard\.html$/
|
|
);
|
|
expect(buildResult.output['dashboard.rsc'].type).toBe('Prerender');
|
|
expect(buildResult.output['dashboard.rsc'].fallback.fsPath).toMatch(
|
|
/server\/app\/dashboard\.rsc$/
|
|
);
|
|
expect(buildResult.output['dashboard/index/index'].type).toBe('Prerender');
|
|
expect(buildResult.output['dashboard/index/index'].fallback.fsPath).toMatch(
|
|
/server\/app\/dashboard\/index\.html$/
|
|
);
|
|
expect(buildResult.output['dashboard/index.rsc'].type).toBe('Prerender');
|
|
expect(buildResult.output['dashboard/index.rsc'].fallback.fsPath).toMatch(
|
|
/server\/app\/dashboard\/index\.rsc$/
|
|
);
|
|
});
|
|
|
|
it('should build with app-dir in edge runtime correctly', async () => {
|
|
const { buildResult } = await runBuildLambda(
|
|
path.join(__dirname, '../fixtures/00-app-dir-edge')
|
|
);
|
|
|
|
const edgeFunctions = new Set();
|
|
|
|
for (const key of Object.keys(buildResult.output)) {
|
|
if (buildResult.output[key].type === 'EdgeFunction') {
|
|
edgeFunctions.add(buildResult.output[key]);
|
|
}
|
|
}
|
|
|
|
expect(edgeFunctions.size).toBe(3);
|
|
expect(buildResult.output['edge']).toBeDefined();
|
|
expect(buildResult.output['index']).toBeDefined();
|
|
expect(buildResult.output['index/index']).toBeDefined();
|
|
});
|
|
|
|
it('should show error from basePath with legacy monorepo build', async () => {
|
|
let error;
|
|
|
|
try {
|
|
await runBuildLambda(path.join(__dirname, 'legacy-monorepo-basepath'));
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
console.error(error);
|
|
|
|
expect(error.message).toBe(
|
|
'basePath can not be used with `builds` in vercel.json, use Project Settings to configure your monorepo instead'
|
|
);
|
|
});
|
|
}
|
|
|
|
it('should build using server build', async () => {
|
|
const origLog = console.log;
|
|
const origError = console.error;
|
|
const caughtLogs = [];
|
|
|
|
console.log = function (...args) {
|
|
caughtLogs.push(args.join(' '));
|
|
origLog.apply(this, args);
|
|
};
|
|
console.error = function (...args) {
|
|
caughtLogs.push(args.join(' '));
|
|
origError.apply(this, args);
|
|
};
|
|
|
|
const {
|
|
workPath,
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'server-build'));
|
|
|
|
console.log = origLog;
|
|
console.error = origError;
|
|
// server mode should not use the next.config.js wrapping
|
|
// for forcing the correct target
|
|
expect(
|
|
await fs.pathExists(
|
|
path.join(workPath, 'next.config.__vercel_builder_backup__.js')
|
|
)
|
|
).toBe(false);
|
|
|
|
expect(await fs.pathExists(path.join(workPath, 'next.config.js'))).toBe(true);
|
|
|
|
expect(output['index']).toBeDefined();
|
|
expect(output['another']).toBeDefined();
|
|
expect(output['dynamic/[slug]']).toBeDefined();
|
|
expect(output['fallback/[slug]']).toBeDefined();
|
|
expect(output['api']).toBeDefined();
|
|
expect(output['api/another']).toBeDefined();
|
|
expect(output['api/blog/[slug]']).toBeDefined();
|
|
expect(output['static']).toBeDefined();
|
|
expect(output['_app']).not.toBeDefined();
|
|
expect(output['_error']).not.toBeDefined();
|
|
expect(output['_document']).not.toBeDefined();
|
|
|
|
expect(output['index'].type).toBe('Lambda');
|
|
expect(output['index'].allowQuery).toBe(undefined);
|
|
expect(output['index'].memory).toBe(512);
|
|
expect(output['index'].maxDuration).toBe(5);
|
|
expect(output['another'].type).toBe('Lambda');
|
|
expect(output['another'].memory).toBe(512);
|
|
expect(output['another'].maxDuration).toBe(5);
|
|
expect(output['another'].allowQuery).toBe(undefined);
|
|
expect(output['dynamic/[slug]'].type).toBe('Lambda');
|
|
expect(output['dynamic/[slug]'].memory).toBe(undefined);
|
|
expect(output['dynamic/[slug]'].maxDuration).toBe(5);
|
|
expect(output['fallback/[slug]'].type).toBe('Prerender');
|
|
expect(output['fallback/[slug]'].allowQuery).toEqual(['slug']);
|
|
expect(output['_next/data/testing-build-id/fallback/[slug].json'].type).toBe(
|
|
'Prerender'
|
|
);
|
|
expect(
|
|
output['_next/data/testing-build-id/fallback/[slug].json'].allowQuery
|
|
).toEqual(['slug']);
|
|
expect(output['fallback/first'].type).toBe('Prerender');
|
|
expect(output['fallback/first'].allowQuery).toEqual([]);
|
|
expect(output['_next/data/testing-build-id/fallback/first.json'].type).toBe(
|
|
'Prerender'
|
|
);
|
|
expect(
|
|
output['_next/data/testing-build-id/fallback/first.json'].allowQuery
|
|
).toEqual([]);
|
|
expect(output['api'].type).toBe('Lambda');
|
|
expect(output['api'].allowQuery).toBe(undefined);
|
|
expect(output['api'].memory).toBe(128);
|
|
expect(output['api'].maxDuration).toBe(5);
|
|
expect(output['api/another'].type).toBe('Lambda');
|
|
expect(output['api/another'].allowQuery).toBe(undefined);
|
|
expect(output['api/blog/[slug]'].type).toBe('Lambda');
|
|
expect(output['api/blog/[slug]'].allowQuery).toBe(undefined);
|
|
expect(output['static'].type).toBe('FileFsRef');
|
|
expect(output['static'].allowQuery).toBe(undefined);
|
|
expect(output['ssg'].type).toBe('Prerender');
|
|
expect(output['ssg'].allowQuery).toEqual([]);
|
|
|
|
expect(output['index'] === output['another']).toBe(true);
|
|
expect(output['dynamic/[slug]'] !== output['fallback/[slug]'].lambda).toBe(
|
|
true
|
|
);
|
|
expect(output['index'] !== output['dynamic/[slug]']).toBe(true);
|
|
expect(output['api/another'] === output['api/blog/[slug]']).toBe(true);
|
|
expect(output['api'] !== output['api/another']).toBe(true);
|
|
expect(
|
|
caughtLogs.some(log =>
|
|
log.includes('WARNING: Unable to find source file for page')
|
|
)
|
|
).toBeFalsy();
|
|
|
|
const lambdas = new Set();
|
|
let totalLambdas = 0;
|
|
|
|
for (const item of Object.values(output)) {
|
|
if (item.type === 'Lambda') {
|
|
totalLambdas += 1;
|
|
lambdas.add(item);
|
|
} else if (item.type === 'Prerender') {
|
|
lambdas.add(item.lambda);
|
|
totalLambdas += 1;
|
|
}
|
|
}
|
|
expect(lambdas.size).toBe(5);
|
|
expect(lambdas.size).toBeLessThan(totalLambdas);
|
|
});
|
|
|
|
it('should build custom error lambda correctly', async () => {
|
|
const {
|
|
buildResult: { output, routes },
|
|
} = await runBuildLambda(path.join(__dirname, 'custom-error-lambda'));
|
|
|
|
expect(output['index']).toBeDefined();
|
|
expect(output['index'].type).toBe('FileFsRef');
|
|
expect(output['index'].allowQuery).toBe(undefined);
|
|
|
|
expect(output['_error']).toBeDefined();
|
|
expect(output['_error'].type).toBe('Lambda');
|
|
expect(output['_error'].allowQuery).toBe(undefined);
|
|
|
|
const notFoundRoute = routes.find(route => {
|
|
return route.dest === '/_error' && route.status === 404;
|
|
});
|
|
|
|
expect(notFoundRoute).toBeTruthy();
|
|
});
|
|
|
|
it('should build initial beforeFiles rewrites', async () => {
|
|
const {
|
|
buildResult: { output, routes },
|
|
} = await runBuildLambda(
|
|
path.join(__dirname, 'initial-before-files-rewrite')
|
|
);
|
|
|
|
expect(output['index']).toBeDefined();
|
|
expect(output['another']).toBeDefined();
|
|
expect(output['dynamic/[slug]']).toBeDefined();
|
|
expect(output['fallback/[slug]']).toBeDefined();
|
|
expect(output['api']).toBeDefined();
|
|
expect(output['api/another']).toBeDefined();
|
|
expect(output['api/blog/[slug]']).toBeDefined();
|
|
expect(output['_app']).not.toBeDefined();
|
|
expect(output['_error']).not.toBeDefined();
|
|
expect(output['_document']).not.toBeDefined();
|
|
|
|
const rewriteRoute = routes.find(route => {
|
|
return route.dest === '/somewhere';
|
|
});
|
|
|
|
expect(rewriteRoute.check).toBe(true);
|
|
expect(rewriteRoute.continue).toBeUndefined();
|
|
});
|
|
|
|
it('Should build the standard example', async () => {
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'standard'));
|
|
expect(output['index']).toBeDefined();
|
|
expect(output.goodbye).toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
const serverlessError = filePaths.some(filePath => filePath.match(/_error/));
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app-.*\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error-.*\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
expect(serverlessError).toBeTruthy();
|
|
});
|
|
|
|
it('Should build the 404-getstaticprops example', async () => {
|
|
const { buildResult } = await runBuildLambda(
|
|
path.join(__dirname, '404-getstaticprops')
|
|
);
|
|
const { output } = buildResult;
|
|
|
|
expect(output['404']).toBeDefined();
|
|
expect(output['404'].type).toBe('FileFsRef');
|
|
expect(output['404'].allowQuery).toBe(undefined);
|
|
expect(output['_next/data/testing-build-id/404.json']).toBeDefined();
|
|
expect(output['_next/data/testing-build-id/404.json'].type).toBe('FileFsRef');
|
|
expect(output['_next/data/testing-build-id/404.json'].allowQuery).toBe(
|
|
undefined
|
|
);
|
|
const filePaths = Object.keys(output);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error-.*\.js$/)
|
|
);
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
});
|
|
|
|
it('Should build the 404-getstaticprops-i18n example', async () => {
|
|
const { buildResult } = await runBuildLambda(
|
|
path.join(__dirname, '404-getstaticprops-i18n')
|
|
);
|
|
const { output, routes } = buildResult;
|
|
|
|
expect(output['en/404']).toBeDefined();
|
|
expect(output['en/404'].type).toBe('FileFsRef');
|
|
expect(output['en/404'].allowQuery).toBe(undefined);
|
|
expect(output['_next/data/testing-build-id/en/404.json']).toBeDefined();
|
|
expect(output['_next/data/testing-build-id/en/404.json'].type).toBe(
|
|
'FileFsRef'
|
|
);
|
|
expect(output['_next/data/testing-build-id/en/404.json'].allowQuery).toBe(
|
|
undefined
|
|
);
|
|
expect(output['fr/404']).toBeDefined();
|
|
expect(output['fr/404'].type).toBe('FileFsRef');
|
|
expect(output['fr/404'].allowQuery).toBe(undefined);
|
|
expect(output['_next/data/testing-build-id/fr/404.json']).toBeDefined();
|
|
expect(output['_next/data/testing-build-id/fr/404.json'].type).toBe(
|
|
'FileFsRef'
|
|
);
|
|
expect(output['_next/data/testing-build-id/fr/404.json'].allowQuery).toBe(
|
|
undefined
|
|
);
|
|
const filePaths = Object.keys(output);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error-.*\.js$/)
|
|
);
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
|
|
const handleErrorIdx = (routes || []).findIndex(r => r.handle === 'error');
|
|
expect(routes[handleErrorIdx + 1].dest).toBe('/$nextLocale/404');
|
|
expect(routes[handleErrorIdx + 1].headers).toBe(undefined);
|
|
});
|
|
|
|
it('Should build the gip-gsp-404 example', async () => {
|
|
const { buildResult } = await runBuildLambda(
|
|
path.join(__dirname, 'gip-gsp-404')
|
|
);
|
|
const { output, routes } = buildResult;
|
|
|
|
const handleErrorIdx = (routes || []).findIndex(r => r.handle === 'error');
|
|
expect(routes[handleErrorIdx + 1].dest).toBe('/404');
|
|
expect(routes[handleErrorIdx + 1].headers).toBe(undefined);
|
|
expect(output['404']).toBeDefined();
|
|
expect(output['404'].type).toBe('Prerender');
|
|
expect(output['_next/data/testing-build-id/404.json']).toBeDefined();
|
|
expect(output['_next/data/testing-build-id/404.json'].type).toBe('Prerender');
|
|
const filePaths = Object.keys(output);
|
|
const serverlessError = filePaths.some(filePath => filePath.match(/_error/));
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app-.*\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error-.*\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
expect(serverlessError).toBeTruthy();
|
|
});
|
|
|
|
it('Should not deploy preview lambdas for static site', async () => {
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'static-site'));
|
|
expect(output['index']).toBeDefined();
|
|
expect(output['index'].type).toBe('FileFsRef');
|
|
expect(output['index'].allowQuery).toBe(undefined);
|
|
|
|
expect(output['another']).toBeDefined();
|
|
expect(output['another'].type).toBe('FileFsRef');
|
|
expect(output['another'].allowQuery).toBe(undefined);
|
|
|
|
expect(output['dynamic']).toBeDefined();
|
|
expect(output['dynamic'].type).toBe('Prerender');
|
|
expect(output['dynamic'].allowQuery).toEqual([]);
|
|
expect(output['dynamic'].lambda).toBeDefined();
|
|
});
|
|
|
|
it('Should opt-out of shared lambdas when routes are detected', async () => {
|
|
if (__dirname.includes('file-system-api')) {
|
|
// Ignore, since `26-mono-repo-404-lambda` is not relevant for the File System API
|
|
return;
|
|
}
|
|
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(
|
|
path.join(__dirname, '../fixtures/26-mono-repo-404-lambda')
|
|
);
|
|
expect(output['packages/webapp/404']).toBeDefined();
|
|
expect(output['packages/webapp/index']).toBeDefined();
|
|
expect(output['packages/webapp/__NEXT_PAGE_LAMBDA_0']).not.toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
});
|
|
|
|
it('Should build the monorepo example', async () => {
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'monorepo'));
|
|
|
|
expect(output['www/index']).not.toBeDefined();
|
|
expect(output['www/__NEXT_PAGE_LAMBDA_0']).toBeDefined();
|
|
expect(output['www/static/test.txt']).toBeDefined();
|
|
expect(output['www/data.txt']).toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
});
|
|
|
|
it('Should build the legacy standard example', async () => {
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'legacy-standard'));
|
|
expect(output.index).toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
});
|
|
|
|
it('Should build the legacy custom dependency test', async () => {
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'legacy-custom-dependency'));
|
|
expect(output.index).toBeDefined();
|
|
});
|
|
|
|
it('Should throw when package.json or next.config.js is not the "src"', async () => {
|
|
try {
|
|
await runBuildLambda(
|
|
path.join(__dirname, 'no-package-json-and-next-config')
|
|
);
|
|
throw new Error('did not throw');
|
|
} catch (err) {
|
|
expect(err.message).toMatch(/package\.json/);
|
|
}
|
|
});
|
|
|
|
it('Should build the static-files test on legacy', async () => {
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'legacy-static-files'));
|
|
expect(output['static/test.txt']).toBeDefined();
|
|
});
|
|
|
|
it('Should build the static-files test', async () => {
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'static-files'));
|
|
expect(output['static/test.txt']).toBeDefined();
|
|
});
|
|
|
|
it('Should build the public-files test', async () => {
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'public-files'));
|
|
expect(output['robots.txt']).toBeDefined();
|
|
expect(output['generated.txt']).toBeDefined();
|
|
});
|
|
|
|
it('Should build the serverless-config example', async () => {
|
|
const {
|
|
workPath,
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'serverless-config'));
|
|
|
|
expect(output.index).not.toBeDefined();
|
|
expect(output.goodbye).not.toBeDefined();
|
|
expect(output.__NEXT_PAGE_LAMBDA_0).toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
const serverlessError = filePaths.some(filePath => filePath.match(/_error/));
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
expect(serverlessError).toBeTruthy();
|
|
|
|
const contents = await fs.readdir(workPath);
|
|
|
|
expect(contents.some(name => name === 'next.config.js')).toBeTruthy();
|
|
expect(
|
|
contents.some(name =>
|
|
name.includes('next.config.__vercel_builder_backup__')
|
|
)
|
|
).toBeTruthy();
|
|
});
|
|
|
|
it('Should build the serverless-config-monorepo-missing example', async () => {
|
|
const {
|
|
workPath,
|
|
buildResult: { output },
|
|
} = await runBuildLambda(
|
|
path.join(__dirname, 'serverless-config-monorepo-missing')
|
|
);
|
|
|
|
expect(output['nested/index']).not.toBeDefined();
|
|
expect(output['nested/goodbye']).not.toBeDefined();
|
|
expect(output['nested/__NEXT_PAGE_LAMBDA_0']).toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
const serverlessError = filePaths.some(filePath => filePath.match(/_error/));
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
expect(serverlessError).toBeTruthy();
|
|
|
|
const contents = await fs.readdir(path.join(workPath, 'nested'));
|
|
|
|
expect(contents.some(name => name === 'next.config.js')).toBeTruthy();
|
|
});
|
|
|
|
it('Should build the serverless-config-monorepo-present example', async () => {
|
|
const {
|
|
workPath,
|
|
buildResult: { output },
|
|
} = await runBuildLambda(
|
|
path.join(__dirname, 'serverless-config-monorepo-present')
|
|
);
|
|
|
|
expect(output['nested/index']).not.toBeDefined();
|
|
expect(output['nested/goodbye']).not.toBeDefined();
|
|
expect(output['nested/__NEXT_PAGE_LAMBDA_0']).toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
const serverlessError = filePaths.some(filePath => filePath.match(/_error/));
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
expect(serverlessError).toBeTruthy();
|
|
|
|
const contents = await fs.readdir(path.join(workPath, 'nested'));
|
|
|
|
expect(contents.some(name => name === 'next.config.js')).toBeTruthy();
|
|
expect(
|
|
contents.some(name =>
|
|
name.includes('next.config.__vercel_builder_backup__')
|
|
)
|
|
).toBeTruthy();
|
|
});
|
|
|
|
it('Should build the serverless-config-async example', async () => {
|
|
let error = null;
|
|
|
|
try {
|
|
await runBuildLambda(path.join(__dirname, 'serverless-config-async'));
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
expect(error).toBe(null);
|
|
});
|
|
|
|
it('Should build the serverless-config-promise example', async () => {
|
|
let error = null;
|
|
|
|
try {
|
|
await runBuildLambda(path.join(__dirname, 'serverless-config-promise'));
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
|
|
expect(error).toBe(null);
|
|
});
|
|
|
|
it('Should build the serverless-config-object example', async () => {
|
|
const {
|
|
workPath,
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'serverless-config-object'));
|
|
|
|
expect(output['index']).toBeDefined();
|
|
expect(output.goodbye).toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
const serverlessError = filePaths.some(filePath => filePath.match(/_error/));
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app-.*\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error-.*\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
expect(serverlessError).toBeTruthy();
|
|
|
|
const contents = await fs.readdir(workPath);
|
|
|
|
expect(contents.some(name => name === 'next.config.js')).toBeTruthy();
|
|
expect(
|
|
contents.some(name =>
|
|
name.includes('next.config.__vercel_builder_backup__')
|
|
)
|
|
).toBeFalsy();
|
|
});
|
|
|
|
it('Should build the serverless-no-config example', async () => {
|
|
const {
|
|
workPath,
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'serverless-no-config'));
|
|
|
|
expect(output['index']).toBeDefined();
|
|
expect(output.goodbye).toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
const serverlessError = filePaths.some(filePath => filePath.match(/_error/));
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app-.*\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error-.*\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
expect(serverlessError).toBeTruthy();
|
|
|
|
const contents = await fs.readdir(workPath);
|
|
|
|
expect(contents.some(name => name === 'next.config.js')).toBeFalsy();
|
|
expect(
|
|
contents.some(name =>
|
|
name.includes('next.config.__vercel_builder_backup__')
|
|
)
|
|
).toBeFalsy();
|
|
});
|
|
|
|
it('Should invoke build command with serverless-no-config', async () => {
|
|
const {
|
|
workPath,
|
|
buildResult: { output },
|
|
} = await runBuildLambda(path.join(__dirname, 'serverless-no-config-build'));
|
|
|
|
expect(output['index']).toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
const serverlessError = filePaths.some(filePath => filePath.match(/_error/));
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app-.*\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error-.*\.js$/)
|
|
);
|
|
const hasBuildFile = await fs.pathExists(
|
|
path.join(__dirname, 'serverless-no-config-build'),
|
|
'.next',
|
|
'world.txt'
|
|
);
|
|
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
expect(serverlessError).toBeTruthy();
|
|
expect(hasBuildFile).toBeTruthy();
|
|
|
|
const contents = await fs.readdir(workPath);
|
|
|
|
expect(contents.some(name => name === 'next.config.js')).toBeFalsy();
|
|
expect(
|
|
contents.some(name =>
|
|
name.includes('next.config.__vercel_builder_backup__')
|
|
)
|
|
).toBeFalsy();
|
|
});
|
|
|
|
it('Should not exceed function limit for large dependencies (server build)', async () => {
|
|
let logs = '';
|
|
|
|
const origLog = console.log;
|
|
|
|
console.log = function (...args) {
|
|
logs += args.join(' ');
|
|
origLog(...args);
|
|
};
|
|
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(
|
|
path.join(__dirname, '../fixtures/00-test-limit-server-build')
|
|
);
|
|
console.log = origLog;
|
|
|
|
expect(output['index']).toBeDefined();
|
|
expect(output['api/chrome']).toBeDefined();
|
|
expect(output['api/chrome-1']).toBeDefined();
|
|
expect(output['api/firebase']).toBeDefined();
|
|
expect(output['api/firebase-1']).toBeDefined();
|
|
expect(output['gssp']).toBeDefined();
|
|
expect(output['gssp-1']).toBeDefined();
|
|
const filePaths = Object.keys(output);
|
|
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app-.*\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error-.*\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
|
|
const lambdas = new Set();
|
|
|
|
filePaths.forEach(filePath => {
|
|
if (output[filePath].type === 'Lambda') {
|
|
lambdas.add(output[filePath]);
|
|
}
|
|
});
|
|
expect(lambdas.size).toBe(3);
|
|
|
|
// this assertion is unstable as `next-server`'s size can change up and down
|
|
// on canary so skipping to prevent random failures.
|
|
// expect(logs).toContain(
|
|
// 'Warning: Max serverless function size of 50 MB compressed or 250 MB uncompressed almost reached'
|
|
// );
|
|
|
|
expect(logs).toContain('node_modules/chrome-aws-lambda/bin');
|
|
});
|
|
|
|
it('Should not exceed function limit for large dependencies (shared lambda)', async () => {
|
|
if (__dirname.includes('file-system-api')) {
|
|
// Test is not relevant for the File System API
|
|
return;
|
|
}
|
|
|
|
let logs = '';
|
|
|
|
const origLog = console.log;
|
|
|
|
console.log = function (...args) {
|
|
logs += args.join(' ');
|
|
origLog(...args);
|
|
};
|
|
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(
|
|
path.join(__dirname, '../fixtures/00-test-limit-shared-lambdas')
|
|
);
|
|
console.log = origLog;
|
|
|
|
expect(output['index']).toBeDefined();
|
|
expect(output['__NEXT_API_LAMBDA_0']).toBeDefined();
|
|
expect(output['__NEXT_API_LAMBDA_1']).toBeDefined();
|
|
expect(output['__NEXT_API_LAMBDA_2']).not.toBeDefined();
|
|
expect(output['__NEXT_PAGE_LAMBDA_0']).toBeDefined();
|
|
expect(output['__NEXT_PAGE_LAMBDA_1']).not.toBeDefined();
|
|
|
|
const filePaths = Object.keys(output);
|
|
|
|
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_app-.*\.js$/)
|
|
);
|
|
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
|
filePath.match(/static.*\/pages\/_error-.*\.js$/)
|
|
);
|
|
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
|
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
|
|
|
const lambdas = new Set();
|
|
|
|
filePaths.forEach(filePath => {
|
|
if (output[filePath].type === 'Lambda') {
|
|
lambdas.add(output[filePath]);
|
|
}
|
|
});
|
|
expect(lambdas.size).toBe(3);
|
|
|
|
expect(logs).toContain(
|
|
'Warning: Max serverless function size of 50 MB compressed or 250 MB uncompressed almost reached'
|
|
);
|
|
expect(logs).toContain('node_modules/chrome-aws-lambda/bin');
|
|
});
|
|
|
|
it('Should provide lambda info when limit is hit (server build)', async () => {
|
|
let logs = '';
|
|
|
|
const origLog = console.log;
|
|
|
|
console.log = function (...args) {
|
|
logs += args.join(' ');
|
|
origLog(...args);
|
|
};
|
|
|
|
try {
|
|
await runBuildLambda(
|
|
path.join(__dirname, 'test-limit-exceeded-server-build')
|
|
);
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
console.log = origLog;
|
|
|
|
expect(logs).toContain(
|
|
'Max serverless function size was exceeded for 2 functions'
|
|
);
|
|
expect(logs).toContain(
|
|
'Max serverless function size of 50 MB compressed or 250 MB uncompressed reached'
|
|
);
|
|
expect(logs).toContain(`Serverless Function's page: api/both.js`);
|
|
expect(logs).toMatch(
|
|
/Large Dependencies.*?Uncompressed size.*?Compressed size/
|
|
);
|
|
expect(logs).toMatch(
|
|
/node_modules\/chrome-aws-lambda\/bin.*?\d{2}.*?MB.*?\d{2}.*?MB/
|
|
);
|
|
expect(logs).toMatch(/node_modules\/@firebase\/firestore.*?\d{1}.*?MB/);
|
|
expect(logs).toMatch(/big-image-1/);
|
|
expect(logs).toMatch(/big-image-2/);
|
|
});
|
|
|
|
it('Should provide lambda info when limit is hit (shared lambdas)', async () => {
|
|
let logs = '';
|
|
|
|
const origLog = console.log;
|
|
|
|
console.log = function (...args) {
|
|
logs += args.join(' ');
|
|
origLog(...args);
|
|
};
|
|
|
|
try {
|
|
await runBuildLambda(
|
|
path.join(__dirname, 'test-limit-exceeded-shared-lambdas')
|
|
);
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
console.log = origLog;
|
|
|
|
expect(logs).toContain(
|
|
'Max serverless function size was exceeded for 1 function'
|
|
);
|
|
expect(logs).toContain(
|
|
'Max serverless function size of 50 MB compressed or 250 MB uncompressed reached'
|
|
);
|
|
expect(logs).toContain(`Serverless Function's page: api/both.js`);
|
|
expect(logs).toMatch(
|
|
/Large Dependencies.*?Uncompressed size.*?Compressed size/
|
|
);
|
|
expect(logs).toMatch(
|
|
/node_modules\/chrome-aws-lambda\/bin.*?\d{2}.*?MB.*?\d{2}.*?MB/
|
|
);
|
|
expect(logs).toMatch(/node_modules\/@firebase\/firestore.*?\d{1}.*?MB/);
|
|
});
|
|
|
|
it('Should provide lambda info when limit is hit for internal pages (server build)', async () => {
|
|
let logs = '';
|
|
|
|
const origLog = console.log;
|
|
|
|
console.log = function (...args) {
|
|
logs += args.join(' ');
|
|
origLog(...args);
|
|
};
|
|
|
|
try {
|
|
await runBuildLambda(
|
|
path.join(__dirname, 'test-limit-exceeded-internal-files-server-build')
|
|
);
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
console.log = origLog;
|
|
|
|
expect(logs).toContain(
|
|
'Max serverless function size of 50 MB compressed or 250 MB uncompressed reached'
|
|
);
|
|
// expect(logs).toContain(`Serverless Function's page: api/firebase.js`);
|
|
expect(logs).toContain(`Serverless Function's page: api/chrome.js`);
|
|
expect(logs).toContain(`Serverless Function's page: api/both.js`);
|
|
expect(logs).toMatch(
|
|
/Large Dependencies.*?Uncompressed size.*?Compressed size/
|
|
);
|
|
expect(logs).toMatch(
|
|
/node_modules\/chrome-aws-lambda\/bin.*?\d{2}.*?MB.*?\d{2}.*?MB/
|
|
);
|
|
expect(logs).toMatch(/node_modules\/@firebase\/firestore.*?\d{1}.*?MB/);
|
|
expect(logs).toMatch(/public\/big-image-1\.jpg/);
|
|
expect(logs).toMatch(/public\/big-image-2\.jpg/);
|
|
});
|
|
|
|
it('Should provide lambda info when limit is hit (uncompressed)', async () => {
|
|
let logs = '';
|
|
|
|
const origLog = console.log;
|
|
|
|
console.log = function (...args) {
|
|
logs += args.join(' ');
|
|
origLog(...args);
|
|
};
|
|
|
|
try {
|
|
await runBuildLambda(
|
|
path.join(__dirname, 'test-limit-exceeded-404-static-files')
|
|
);
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
console.log = origLog;
|
|
|
|
expect(logs).toContain(
|
|
'Max serverless function size was exceeded for 1 function'
|
|
);
|
|
expect(logs).toContain(
|
|
'Max serverless function size of 50 MB compressed or 250 MB uncompressed reached'
|
|
);
|
|
expect(logs).toContain(`Serverless Function's page: api/hello.js`);
|
|
expect(logs).toMatch(
|
|
/Large Dependencies.*?Uncompressed size.*?Compressed size/
|
|
);
|
|
expect(logs).toMatch(/data\.txt/);
|
|
expect(logs).toMatch(/\.next\/server\/pages/);
|
|
});
|
|
|
|
it('Should de-dupe correctly when limit is close (uncompressed)', async () => {
|
|
const origLog = console.log;
|
|
const origError = console.error;
|
|
const caughtLogs = [];
|
|
|
|
console.log = function (...args) {
|
|
caughtLogs.push(args.join(' '));
|
|
origLog.apply(this, args);
|
|
};
|
|
console.error = function (...args) {
|
|
caughtLogs.push(args.join(' '));
|
|
origError.apply(this, args);
|
|
};
|
|
|
|
const {
|
|
buildResult: { output },
|
|
} = await runBuildLambda(
|
|
path.join(__dirname, 'test-limit-large-uncompressed-files')
|
|
);
|
|
|
|
console.log = origLog;
|
|
console.error = origError;
|
|
|
|
expect(output['index']).toBeDefined();
|
|
expect(output['another']).toBeDefined();
|
|
expect(output['api/hello']).toBeDefined();
|
|
expect(output['api/hello-1']).toBeDefined();
|
|
expect(output['api/hello-2']).toBeDefined();
|
|
expect(output['api/hello-3']).toBeDefined();
|
|
expect(output['api/hello-4']).toBeDefined();
|
|
expect(output['_app']).not.toBeDefined();
|
|
expect(output['_error']).not.toBeDefined();
|
|
expect(output['_document']).not.toBeDefined();
|
|
|
|
expect(output['index'] === output['another']).toBe(true);
|
|
expect(output['index'] !== output['api/hello']).toBe(true);
|
|
expect(output['api/hello'] === output['api/hello-1']).toBe(true);
|
|
expect(output['api/hello'] === output['api/hello-2']).toBe(true);
|
|
expect(output['api/hello'] === output['api/hello-3']).toBe(true);
|
|
expect(output['api/hello'] === output['api/hello-4']).toBe(true);
|
|
|
|
expect(
|
|
caughtLogs.some(log =>
|
|
log.includes('WARNING: Unable to find source file for page')
|
|
)
|
|
).toBeFalsy();
|
|
|
|
const lambdas = new Set();
|
|
let totalLambdas = 0;
|
|
|
|
for (const item of Object.values(output)) {
|
|
if (item.type === 'Lambda') {
|
|
totalLambdas += 1;
|
|
lambdas.add(item);
|
|
} else if (item.type === 'Prerender') {
|
|
lambdas.add(item.lambda);
|
|
totalLambdas += 1;
|
|
}
|
|
}
|
|
expect(lambdas.size).toBe(2);
|
|
expect(lambdas.size).toBeLessThan(totalLambdas);
|
|
});
|