Skip to content
This repository was archived by the owner on Jan 28, 2025. It is now read-only.

Commit 97c6405

Browse files
Support static directory upload to S3 (#54)
* feat: add support for static directory upload to S3 * feat: add debug flag exposed via DEBUG=sls-next:*
1 parent 6739a5f commit 97c6405

File tree

14 files changed

+799
-361
lines changed

14 files changed

+799
-361
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ custom:
119119

120120
With this approach you could have a CloudFront distribution in front of the bucket and use a custom domain in the assetPrefix.
121121

122+
| Plugin config key | Default Value | Description |
123+
| ----------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
124+
| assetsBucketName | \<empty\> | Creates an S3 bucket with the name provided. The bucket will be used for uploading next static assets |
125+
| staticDir | \<empty\> | Directory with static assets to be uploaded to S3, typically a directory named `static`, but it can be any other name. Requires a bucket provided via the `assetPrefix` described above or the `assetsBucketName` plugin config. |
126+
| uploadBuildAssets | true | In the unlikely event that you only want to upload the `staticDir`, set this to `false` |
127+
122128
## Deploying
123129

124130
`serverless deploy`

__tests__/index.test.js

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
1-
const path = require("path");
21
const ServerlessPluginBuilder = require("../utils/test/ServerlessPluginBuilder");
3-
const parsedNextConfigurationFactory = require("../utils/test/parsedNextConfigurationFactory");
4-
const uploadStaticAssetsToS3 = require("../lib/uploadStaticAssetsToS3");
52
const displayStackOutput = require("../lib/displayStackOutput");
6-
const parseNextConfiguration = require("../lib/parseNextConfiguration");
73
const build = require("../lib/build");
84
const NextPage = require("../classes/NextPage");
95
const PluginBuildDir = require("../classes/PluginBuildDir");
106

117
jest.mock("js-yaml");
128
jest.mock("../lib/build");
139
jest.mock("../lib/parseNextConfiguration");
14-
jest.mock("../lib/uploadStaticAssetsToS3");
1510
jest.mock("../lib/displayStackOutput");
1611
jest.mock("../utils/logger");
1712

@@ -159,66 +154,6 @@ describe("ServerlessNextJsPlugin", () => {
159154
});
160155
});
161156

162-
describe("#uploadStaticAssets", () => {
163-
it("should NOT call uploadStaticAssetsToS3 when there isn't a bucket available", () => {
164-
parseNextConfiguration.mockReturnValueOnce(
165-
parsedNextConfigurationFactory({}, null)
166-
);
167-
168-
const plugin = new ServerlessPluginBuilder().build();
169-
170-
return plugin.uploadStaticAssets().then(() => {
171-
expect(uploadStaticAssetsToS3).not.toBeCalled();
172-
});
173-
});
174-
175-
it("should call uploadStaticAssetsToS3 with bucketName and next static dir", () => {
176-
const distDir = "build";
177-
parseNextConfiguration.mockReturnValueOnce(
178-
parsedNextConfigurationFactory({
179-
distDir
180-
})
181-
);
182-
183-
uploadStaticAssetsToS3.mockResolvedValueOnce("Assets Uploaded");
184-
185-
const plugin = new ServerlessPluginBuilder().build();
186-
187-
return plugin.uploadStaticAssets().then(() => {
188-
expect(uploadStaticAssetsToS3).toBeCalledWith({
189-
staticAssetsPath: path.join("/path/to/next", distDir, "static"),
190-
bucketName: "my-bucket",
191-
providerRequest: expect.any(Function)
192-
});
193-
});
194-
});
195-
196-
it("should call uploadStaticAssetsToS3 with bucketName from plugin config", () => {
197-
const distDir = "build";
198-
parseNextConfiguration.mockReturnValueOnce(
199-
parsedNextConfigurationFactory({
200-
distDir
201-
})
202-
);
203-
204-
uploadStaticAssetsToS3.mockResolvedValueOnce("Assets Uploaded");
205-
206-
const plugin = new ServerlessPluginBuilder()
207-
.withNextCustomConfig({
208-
assetsBucketName: "custom-bucket"
209-
})
210-
.build();
211-
212-
return plugin.uploadStaticAssets().then(() => {
213-
expect(uploadStaticAssetsToS3).toBeCalledWith({
214-
staticAssetsPath: path.join("/path/to/next", distDir, "static"),
215-
bucketName: "custom-bucket",
216-
providerRequest: expect.any(Function)
217-
});
218-
});
219-
});
220-
});
221-
222157
describe("#printStackOutput", () => {
223158
it("should call displayStackOutput with awsInfo", () => {
224159
const awsInfo = {

index.js

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"use strict";
22

33
const path = require("path");
4-
const uploadStaticAssetsToS3 = require("./lib/uploadStaticAssetsToS3");
54
const displayStackOutput = require("./lib/displayStackOutput");
65
const parseNextConfiguration = require("./lib/parseNextConfiguration");
76
const build = require("./lib/build");
87
const PluginBuildDir = require("./classes/PluginBuildDir");
98
const addAssetsBucketForDeployment = require("./lib/addAssetsBucketForDeployment");
9+
const uploadStaticAssets = require("./lib/uploadStaticAssets");
1010

1111
class ServerlessNextJsPlugin {
1212
constructor(serverless, options) {
@@ -19,7 +19,7 @@ class ServerlessNextJsPlugin {
1919
this.pluginBuildDir = new PluginBuildDir(this.nextConfigDir);
2020

2121
this.addAssetsBucketForDeployment = addAssetsBucketForDeployment.bind(this);
22-
this.uploadStaticAssets = this.uploadStaticAssets.bind(this);
22+
this.uploadStaticAssets = uploadStaticAssets.bind(this);
2323
this.printStackOutput = this.printStackOutput.bind(this);
2424
this.buildNextPages = this.buildNextPages.bind(this);
2525
this.removePluginBuildDir = this.removePluginBuildDir.bind(this);
@@ -74,30 +74,6 @@ class ServerlessNextJsPlugin {
7474
this.serverless.service.setFunctionNames();
7575
}
7676

77-
uploadStaticAssets() {
78-
let { nextConfiguration, staticAssetsBucket } = this.configuration;
79-
80-
const bucketNameFromConfig = this.getPluginConfigValue("assetsBucketName");
81-
82-
if (bucketNameFromConfig) {
83-
staticAssetsBucket = bucketNameFromConfig;
84-
}
85-
86-
if (!staticAssetsBucket) {
87-
return Promise.resolve();
88-
}
89-
90-
return uploadStaticAssetsToS3({
91-
staticAssetsPath: path.join(
92-
this.nextConfigDir,
93-
nextConfiguration.distDir,
94-
"static"
95-
),
96-
providerRequest: this.providerRequest,
97-
bucketName: staticAssetsBucket
98-
});
99-
}
100-
10177
printStackOutput() {
10278
const awsInfo = this.serverless.pluginManager.getPlugins().find(plugin => {
10379
return plugin.constructor.name === "AwsInfo";

lib/__tests__/addAssetsBucketForDeployment.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,25 @@ describe("addAssetsBucketForDeployment", () => {
2727
jest.clearAllMocks();
2828
});
2929

30+
it("should error if staticDir provided but no bucket", () => {
31+
expect.assertions(1);
32+
33+
const pluginWithoutBucket = new ServerlessPluginBuilder()
34+
.withNextCustomConfig({
35+
nextConfigDir: "./",
36+
staticDir: "./static"
37+
})
38+
.build();
39+
40+
parseNextConfiguration.mockReturnValueOnce(
41+
parsedNextConfigurationFactory({ staticAssetsBucket: null }, null)
42+
);
43+
44+
return addAssetsBucketForDeployment.call(pluginWithoutBucket).catch(err => {
45+
expect(err.message).toContain("staticDir requires a bucket. See");
46+
});
47+
});
48+
3049
it("should not call addS3BucketToResources if a staticAssetsBucket is not available", () => {
3150
expect.assertions(1);
3251
parseNextConfiguration.mockReturnValueOnce(

lib/__tests__/uploadStaticAssetsToS3.test.js

Lines changed: 0 additions & 211 deletions
This file was deleted.

0 commit comments

Comments
 (0)