Skip to content

Commit 1c14017

Browse files
danielcondemarinsclaughl
authored andcommitted
all basic functionality done
1 parent 239de5b commit 1c14017

File tree

9 files changed

+146
-41
lines changed

9 files changed

+146
-41
lines changed
Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,31 @@
1+
import AWS from "aws-sdk";
12
import path from "path";
23
import fse from "fs-extra";
3-
import mime from "mime-types";
4-
import AWS from "aws-sdk";
54
import readDirectoryFiles from "./lib/readDirectoryFiles";
5+
import filterOutDirectories from "./lib/filterOutDirectories";
6+
import { IMMUTABLE_CACHE_CONTROL_HEADER } from "./lib/constants";
7+
import S3ClientFactory, { S3Client } from "./lib/s3";
68

79
type UploadStaticAssetsOptions = {
810
bucketName: string;
911
nextConfigDir: string;
1012
};
1113

14+
const uploadPublicOrStaticDirectory = async (
15+
s3: S3Client,
16+
directory: "public" | "static",
17+
nextConfigDir: string
18+
): Promise<Promise<AWS.S3.ManagedUpload.SendData>[]> => {
19+
const files = await readDirectoryFiles(path.join(nextConfigDir, directory));
20+
21+
return files.filter(filterOutDirectories).map(fileItem =>
22+
s3.uploadFile({
23+
filePath: fileItem.path,
24+
s3Key: path.posix.relative(path.resolve(nextConfigDir), fileItem.path)
25+
})
26+
);
27+
};
28+
1229
const filePathToS3Key = (filePath: string): string => {
1330
const relevantFilePathPart = filePath.substring(
1431
filePath.indexOf(".next" + path.sep)
@@ -18,10 +35,12 @@ const filePathToS3Key = (filePath: string): string => {
1835

1936
const uploadStaticAssets = async (
2037
options: UploadStaticAssetsOptions
21-
): Promise<void> => {
38+
): Promise<AWS.S3.ManagedUpload.SendData> => {
2239
const { bucketName, nextConfigDir } = options;
2340

24-
const s3 = new AWS.S3();
41+
const s3 = S3ClientFactory({
42+
bucketName
43+
});
2544

2645
const dotNextDirectory = path.join(nextConfigDir, ".next");
2746

@@ -33,54 +52,56 @@ const uploadStaticAssets = async (
3352
path.join(dotNextDirectory, "static", BUILD_ID)
3453
);
3554

36-
const uploadTasks = buildStaticFiles
37-
.filter(item => !item.stats.isDirectory())
55+
const nextBuildFileUploads = buildStaticFiles
56+
.filter(filterOutDirectories)
3857
.map(async fileItem => {
39-
const fileBody = await fse.readFile(fileItem.path);
40-
4158
const s3Key = filePathToS3Key(fileItem.path);
4259

43-
return s3
44-
.upload({
45-
Bucket: bucketName,
46-
Key: s3Key,
47-
Body: fileBody,
48-
ContentType:
49-
mime.lookup(path.basename(fileItem.path)) ||
50-
"application/octet-stream",
51-
CacheControl: "public, max-age=31536000, immutable"
52-
})
53-
.promise();
60+
return s3.uploadFile({
61+
s3Key,
62+
filePath: fileItem.path,
63+
cacheControl: IMMUTABLE_CACHE_CONTROL_HEADER
64+
});
5465
});
5566

5667
const pagesManifest = await fse.readJSON(
5768
path.join(dotNextDirectory, "serverless/pages-manifest.json")
5869
);
5970

60-
const htmlPageUploadTasks = Object.values(pagesManifest)
71+
const htmlPageUploads = Object.values(pagesManifest)
6172
.filter(pageFile => (pageFile as string).endsWith(".html"))
62-
.map(async pageFile => {
63-
const fileBody = await fse.readFile(
64-
path.join(dotNextDirectory, `serverless/${pageFile}`)
73+
.map(relativePageFilePath => {
74+
const pageFilePath = path.join(
75+
dotNextDirectory,
76+
`serverless/${relativePageFilePath}`
6577
);
6678

67-
return s3
68-
.upload({
69-
Bucket: bucketName,
70-
Key: `static-pages/${(pageFile as string).replace(/^pages\//, "")}`,
71-
Body: fileBody,
72-
ContentType: "text/html",
73-
CacheControl: undefined
74-
})
75-
.promise();
79+
return s3.uploadFile({
80+
s3Key: `static-pages/${relativePageFilePath.replace(/^pages\//, "")}`,
81+
filePath: pageFilePath
82+
});
7683
});
7784

78-
uploadTasks.concat(htmlPageUploadTasks);
85+
const publicDirUploads = await uploadPublicOrStaticDirectory(
86+
s3,
87+
"public",
88+
nextConfigDir
89+
);
90+
91+
const staticDirUploads = await uploadPublicOrStaticDirectory(
92+
s3,
93+
"static",
94+
nextConfigDir
95+
);
96+
97+
const allUploads = [
98+
...nextBuildFileUploads,
99+
...htmlPageUploads,
100+
...publicDirUploads,
101+
...staticDirUploads
102+
];
79103

80-
await Promise.all(uploadTasks);
81-
// read public/ folder and upload files
82-
// read static/ folder and upload files
83-
// get JSON data files from prerender manifest
104+
return Promise.all(allUploads);
84105
};
85106

86107
export default uploadStaticAssets;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { Item } from "klaw";
2+
3+
export default (fileItem: Item): boolean => !fileItem.stats.isDirectory();
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import mime from "mime-types";
2+
import path from "path";
3+
4+
export default (filePath: string): string =>
5+
mime.lookup(path.basename(filePath)) || "application/octet-stream";
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import getMimeType from "./getMimeType";
2+
import fse from "fs-extra";
3+
import AWS from "aws-sdk";
4+
5+
type S3ClientFactoryOptions = {
6+
bucketName: string;
7+
};
8+
9+
type UploadFileOptions = {
10+
filePath: string;
11+
cacheControl?: string;
12+
s3Key?: string;
13+
};
14+
15+
export type S3Client = {
16+
uploadFile: (
17+
options: UploadFileOptions
18+
) => Promise<AWS.S3.ManagedUpload.SendData>;
19+
};
20+
21+
export default ({ bucketName }: S3ClientFactoryOptions): S3Client => {
22+
const s3 = new AWS.S3();
23+
24+
return {
25+
uploadFile: async (
26+
options: UploadFileOptions
27+
): Promise<AWS.S3.ManagedUpload.SendData> => {
28+
const { filePath, cacheControl, s3Key } = options;
29+
30+
const fileBody = await fse.readFile(filePath);
31+
32+
return s3
33+
.upload({
34+
Bucket: bucketName,
35+
Key: s3Key || filePath,
36+
Body: fileBody,
37+
ContentType: getMimeType(filePath),
38+
CacheControl: cacheControl || undefined
39+
})
40+
.promise();
41+
}
42+
};
43+
};

packages/s3-static-assets/tests/fixtures/basic-next-app/public/robots.txt

Whitespace-only changes.

packages/s3-static-assets/tests/fixtures/basic-next-app/public/scripts/test-script.js

Whitespace-only changes.

packages/s3-static-assets/tests/fixtures/basic-next-app/static/robots.txt

Whitespace-only changes.

packages/s3-static-assets/tests/fixtures/basic-next-app/static/scripts/test-script.js

Whitespace-only changes.

packages/s3-static-assets/tests/upload-assets.test.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import path from "path";
22
import uploadStaticAssets from "../src/index";
3-
import { IMMUTABLE_CACHE_CONTROL_HEADER } from "../src/constants";
3+
import { IMMUTABLE_CACHE_CONTROL_HEADER } from "../src/lib/constants";
44
import { mockUpload } from "aws-sdk";
55

66
declare module "aws-sdk" {
@@ -16,8 +16,6 @@ describe("Upload assets tests", () => {
1616
});
1717

1818
it("uploads any contents inside build directory specified in BUILD_ID", async () => {
19-
expect(mockUpload).toBeCalledTimes(2);
20-
2119
expect(mockUpload).toBeCalledWith({
2220
Bucket: "test-bucket-name",
2321
Key: "_next/static/a_test_build_id/two.js",
@@ -47,10 +45,45 @@ describe("Upload assets tests", () => {
4745
expect(mockUpload).toBeCalledWith(
4846
expect.objectContaining({
4947
Key: "static-pages/todos/terms/[section].html",
50-
Body: expect.any(Buffer),
5148
ContentType: "text/html",
5249
CacheControl: undefined
5350
})
5451
);
5552
});
53+
54+
it("uploads files in the public folder", async () => {
55+
expect(mockUpload).toBeCalledWith(
56+
expect.objectContaining({
57+
Key: "public/robots.txt",
58+
ContentType: "text/plain",
59+
CacheControl: undefined
60+
})
61+
);
62+
63+
expect(mockUpload).toBeCalledWith(
64+
expect.objectContaining({
65+
Key: "public/scripts/test-script.js",
66+
ContentType: "application/javascript",
67+
CacheControl: undefined
68+
})
69+
);
70+
});
71+
72+
it("uploads files in the static folder", async () => {
73+
expect(mockUpload).toBeCalledWith(
74+
expect.objectContaining({
75+
Key: "static/robots.txt",
76+
ContentType: "text/plain",
77+
CacheControl: undefined
78+
})
79+
);
80+
81+
expect(mockUpload).toBeCalledWith(
82+
expect.objectContaining({
83+
Key: "static/scripts/test-script.js",
84+
ContentType: "application/javascript",
85+
CacheControl: undefined
86+
})
87+
);
88+
});
5689
});

0 commit comments

Comments
 (0)