Skip to content

Commit

Permalink
feat(serverless-component): implement getStaticProps / getStaticPaths…
Browse files Browse the repository at this point in the history
… [fallback: false] (serverless-nextjs#390)

Co-authored-by: Daniel <[email protected]>
  • Loading branch information
2 people authored and sclaughl committed Jul 16, 2020
1 parent 72e928d commit c796508
Show file tree
Hide file tree
Showing 54 changed files with 508 additions and 195 deletions.
386 changes: 206 additions & 180 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
"@babel/preset-typescript": "^7.9.0",
"@sls-next/lambda-at-edge": "file:packages/lambda-at-edge",
"@types/jest": "^25.2.1",
"@types/react": "^16.9.35",
"@types/react-dom": "^16.9.8",
"@typescript-eslint/eslint-plugin": "^2.28.0",
"@typescript-eslint/parser": "^2.28.0",
"adm-zip": "^0.4.13",
Expand Down
2 changes: 1 addition & 1 deletion packages/lambda-at-edge-compat/package-lock.json

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

4 changes: 4 additions & 0 deletions packages/lambda-at-edge/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ class Builder {
);
}
}
),
fse.copy(
join(this.nextConfigDir, ".next/prerender-manifest.json"),
join(this.outputDir, DEFAULT_LAMBDA_CODE_DIR, "prerender-manifest.json")
)
]);
}
Expand Down
15 changes: 11 additions & 4 deletions packages/lambda-at-edge/src/default-handler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
// @ts-ignore
import PrerenderManifest from "./prerender-manifest.json";
// @ts-ignore
import Manifest from "./manifest.json";
import { PrerenderManifest as PrerenderManifestType } from "next/dist/build/index";
import lambdaAtEdgeCompat from "next-aws-cloudfront";
import {
CloudFrontRequest,
Expand Down Expand Up @@ -49,19 +52,23 @@ export const handler = async (
): Promise<CloudFrontResultResponse | CloudFrontRequest> => {
const request = event.Records[0].cf.request;
const uri = normaliseUri(request.uri);
const manifest = Manifest as OriginRequestDefaultHandlerManifest;
const manifest: OriginRequestDefaultHandlerManifest = Manifest;
const prerenderManifest: PrerenderManifestType = PrerenderManifest;
const { pages, publicFiles } = manifest;

const isStaticPage = pages.html.nonDynamic[uri];
const isPublicFile = publicFiles[uri];
const isPrerenderedPage = prerenderManifest.routes[uri]; // prerendered pages are also static pages like "pages.html" above, but are defined in the prerender-manifest

const origin = request.origin as CloudFrontOrigin;
const s3Origin = origin.s3 as CloudFrontS3Origin;

if (isStaticPage || isPublicFile) {
s3Origin.path = isStaticPage ? "/static-pages" : "/public";
const isHTMLPage = isStaticPage || isPrerenderedPage;

if (isHTMLPage || isPublicFile) {
s3Origin.path = isHTMLPage ? "/static-pages" : "/public";

if (isStaticPage) {
if (isHTMLPage) {
request.uri = uri + ".html";
}

Expand Down
Empty file.
3 changes: 2 additions & 1 deletion packages/lambda-at-edge/tests/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ describe("Builder Tests", () => {
"index.js",
"manifest.json",
"node_modules",
"pages"
"pages",
"prerender-manifest.json"
]);

expect(compatLayerIncluded).toEqual(true);
Expand Down
25 changes: 17 additions & 8 deletions packages/lambda-at-edge/tests/default-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ jest.mock(
}
);

jest.mock(
"../src/prerender-manifest.json",
() => require("./fixtures/prerender-manifest.json"),
{
virtual: true
}
);

const mockPageRequire = (mockPagePath: string): void => {
jest.mock(
`../src/${mockPagePath}`,
Expand All @@ -24,15 +32,16 @@ describe("Lambda@Edge", () => {
describe("Routing", () => {
describe("HTML pages routing", () => {
it.each`
path | expectedPage
${"/"} | ${"/index.html"}
${"/index"} | ${"/index.html"}
${"/terms"} | ${"/terms.html"}
${"/users/batman"} | ${"/users/[user].html"}
${"/users/test/catch/all"} | ${"/users/[...user].html"}
${"/john/123"} | ${"/[username]/[id].html"}
path | expectedPage
${"/"} | ${"/index.html"}
${"/index"} | ${"/index.html"}
${"/terms"} | ${"/terms.html"}
${"/users/batman"} | ${"/users/[user].html"}
${"/users/test/catch/all"} | ${"/users/[...user].html"}
${"/john/123"} | ${"/[username]/[id].html"}
${"/tests/prerender-manifest/example-static-page"} | ${"/tests/prerender-manifest/example-static-page.html"}
`(
"serves page $expectedPage for path $path",
"serves page $expectedPage from S3 for path $path",
async ({ path, expectedPage }) => {
const event = createCloudFrontEvent({
uri: path,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 2,
"routes": {
"/": {
"initialRevalidateSeconds": false,
"srcRoute": null,
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/index.json"
}
}
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 2,
"routes": {
"/": {
"initialRevalidateSeconds": false,
"srcRoute": null,
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/index.json"
}
}
}
23 changes: 23 additions & 0 deletions packages/lambda-at-edge/tests/fixtures/prerender-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"version": 2,
"routes": {
"/tests/prerender-manifest/example-static-page": {
"initialRevalidateSeconds": false,
"srcRoute": "/tests/prerender-manifest/[staticPageName]",
"dataRoute": "/_next/data/test-build-id/tests/prerender-manifest/example-static-page.json"
}
},
"dynamicRoutes": {
"/tests/prerender-manifest/[staticPageName]": {
"routeRegex": "^/tests/prerender-manifest/(?:([^/]+?))/?$",
"dataRoute": "/_next/data/test-build-id/tests/prerender-manifest/[staticPageName].json",
"fallback": false,
"dataRouteRegex": "^/_next/data/test-build-id/tests/prerender-manifest/(?:([^/]+?)).json/?$"
}
},
"preview": {
"previewModeId": "test-preview-mode-id",
"previewModeSigningKey": "test-preview-mode-signing-key",
"previewModeEncryptionKey": "test-preview-mode-enc-key"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 2,
"routes": {
"/": {
"initialRevalidateSeconds": false,
"srcRoute": null,
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/index.json"
}
}
}
Empty file.
42 changes: 42 additions & 0 deletions packages/s3-static-assets/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import filterOutDirectories from "./lib/filterOutDirectories";
import { IMMUTABLE_CACHE_CONTROL_HEADER } from "./lib/constants";
import S3ClientFactory, { Credentials } from "./lib/s3";
import pathToPosix from "./lib/pathToPosix";
import { PrerenderManifest } from "next/dist/build/index";
import getPublicAssetCacheControl, {
PublicDirectoryCache
} from "./lib/getPublicAssetCacheControl";
Expand Down Expand Up @@ -69,6 +70,45 @@ const uploadStaticAssets = async (
});
});

const prerenderManifest: PrerenderManifest = await fse.readJSON(
path.join(dotNextDirectory, "prerender-manifest.json")
);

const prerenderManifestJSONPropFileUploads = Object.keys(
prerenderManifest.routes
).map(key => {
const pageFilePath = pathToPosix(
path.join(
dotNextDirectory,
`serverless/pages/${
key.endsWith("/") ? key + "index.json" : key + ".json"
}`
)
);

return s3.uploadFile({
s3Key: prerenderManifest.routes[key].dataRoute.slice(1),
filePath: pageFilePath
});
});

const prerenderManifestHTMLPageUploads = Object.keys(
prerenderManifest.routes
).map(key => {
const relativePageFilePath = key.endsWith("/")
? path.posix.join(key, "index.html")
: key + ".html";

const pageFilePath = pathToPosix(
path.join(dotNextDirectory, `serverless/pages/${relativePageFilePath}`)
);

return s3.uploadFile({
s3Key: path.posix.join("static-pages", relativePageFilePath),
filePath: pageFilePath
});
});

const uploadPublicOrStaticDirectory = async (
directory: "public" | "static",
publicDirectoryCache?: PublicDirectoryCache
Expand Down Expand Up @@ -106,6 +146,8 @@ const uploadStaticAssets = async (
const allUploads = [
...buildStaticFileUploads, // .next/static
...htmlPageUploads, // prerendered HTML pages
...prerenderManifestJSONPropFileUploads, // SSG JSON files
...prerenderManifestHTMLPageUploads, // SSG HTML files
...publicDirUploads, // app public dir
...staticDirUploads // app static dir
];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": 2,
"routes": {
"/": {
"initialRevalidateSeconds": false,
"srcRoute": null,
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/index.json"
},
"/todos/terms/a": {
"initialRevalidateSeconds": false,
"srcRoute": "/todos/terms/[section]",
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/todos/terms/a.json"
},
"/todos/terms/b": {
"initialRevalidateSeconds": false,
"srcRoute": "/todos/terms/[section]",
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/todos/terms/b.json"
}
}
}
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": 2,
"routes": {
"/": {
"initialRevalidateSeconds": false,
"srcRoute": null,
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/index.json"
},
"/todos/terms/a": {
"initialRevalidateSeconds": false,
"srcRoute": "/todos/terms/[section]",
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/todos/terms/a.json"
},
"/todos/terms/b": {
"initialRevalidateSeconds": false,
"srcRoute": "/todos/terms/[section]",
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/todos/terms/b.json"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": 2,
"routes": {
"/": {
"initialRevalidateSeconds": false,
"srcRoute": null,
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/index.json"
},
"/todos/terms/a": {
"initialRevalidateSeconds": false,
"srcRoute": "/todos/terms/[section]",
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/todos/terms/a.json"
},
"/todos/terms/b": {
"initialRevalidateSeconds": false,
"srcRoute": "/todos/terms/[section]",
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/todos/terms/b.json"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 2,
"routes": {
"/todos/terms": {
"initialRevalidateSeconds": false,
"srcRoute": null,
"dataRoute": "/_next/data/test-build-id/todos/terms.json"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": 2,
"routes": {
"/": {
"initialRevalidateSeconds": false,
"srcRoute": null,
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/index.json"
},
"/todos/terms/a": {
"initialRevalidateSeconds": false,
"srcRoute": "/todos/terms/[section]",
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/todos/terms/a.json"
},
"/todos/terms/b": {
"initialRevalidateSeconds": false,
"srcRoute": "/todos/terms/[section]",
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/todos/terms/b.json"
}
}
}
Empty file.
50 changes: 49 additions & 1 deletion packages/s3-static-assets/tests/upload-assets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ describe.each`
});
});

it("uploads prerendered HTML pages specified in pages manifest", async () => {
it("uploads HTML pages specified in pages manifest", async () => {
await upload(nextConfigDir, nextStaticDir);

expect(mockUpload).toBeCalledWith(
Expand All @@ -149,6 +149,54 @@ describe.each`
);
});

it("uploads staticProps JSON files specified in prerender manifest", async () => {
await upload(nextConfigDir, nextStaticDir);

expect(mockUpload).toBeCalledWith(
expect.objectContaining({
Key: "_next/data/zsWqBqLjpgRmswfQomanp/index.json",
ContentType: "application/json",
CacheControl: undefined
})
);

expect(mockUpload).toBeCalledWith(
expect.objectContaining({
Key: "_next/data/zsWqBqLjpgRmswfQomanp/todos/terms/a.json",
ContentType: "application/json",
CacheControl: undefined
})
);

expect(mockUpload).toBeCalledWith(
expect.objectContaining({
Key: "_next/data/zsWqBqLjpgRmswfQomanp/todos/terms/b.json",
ContentType: "application/json",
CacheControl: undefined
})
);
});

it("uploads prerendered HTML pages specified in prerender manifest", async () => {
await upload(nextConfigDir, nextStaticDir);

expect(mockUpload).toBeCalledWith(
expect.objectContaining({
Key: "static-pages/todos/terms/a.html",
ContentType: "text/html",
CacheControl: undefined
})
);

expect(mockUpload).toBeCalledWith(
expect.objectContaining({
Key: "static-pages/todos/terms/b.html",
ContentType: "text/html",
CacheControl: undefined
})
);
});

it("uploads files in the public folder", async () => {
await upload(nextConfigDir, nextStaticDir);

Expand Down
Loading

0 comments on commit c796508

Please sign in to comment.