sst 2.0.0-rc.50 → 2.0.0-rc.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/commands/deploy.js +5 -1
- package/cli/commands/dev.js +29 -37
- package/cli/commands/telemetry.d.ts +15 -0
- package/cli/commands/telemetry.js +17 -0
- package/cli/program.js +1 -1
- package/cli/sst.js +2 -0
- package/cli/ui/deploy.js +5 -12
- package/constructs/AstroSite.d.ts +0 -1
- package/constructs/AstroSite.js +31 -46
- package/constructs/Job.js +6 -2
- package/constructs/NextjsSite.d.ts +2 -4
- package/constructs/NextjsSite.js +63 -101
- package/constructs/RemixSite.d.ts +0 -2
- package/constructs/RemixSite.js +4 -12
- package/constructs/SolidStartSite.d.ts +0 -1
- package/constructs/SolidStartSite.js +29 -41
- package/constructs/SsrSite.d.ts +28 -64
- package/constructs/SsrSite.js +78 -129
- package/constructs/StaticSite.d.ts +30 -50
- package/constructs/StaticSite.js +80 -105
- package/credentials.js +19 -0
- package/package.json +2 -1
- package/project.js +3 -1
- package/sst.mjs +170 -104
- package/stacks/build.js +1 -1
- package/stacks/deploy.js +2 -2
- package/stacks/monitor.d.ts +1 -0
- package/stacks/monitor.js +9 -1
- package/support/job-invoker/index.mjs +26 -0
- package/support/astro-site-html-stub/index.html +0 -99
- package/support/nextjs-site-html-stub/index.html +0 -99
- package/support/remix-site-html-stub/index.html +0 -99
- package/support/solid-start-site-html-stub/index.html +0 -99
- package/support/static-site-stub/index.html +0 -90
package/cli/commands/deploy.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useSTSIdentity } from "../../credentials.js";
|
|
2
2
|
import { Colors } from "../colors.js";
|
|
3
|
+
import fs from "fs/promises";
|
|
4
|
+
import path from "path";
|
|
3
5
|
export const deploy = (program) => program.command("deploy [filter]", "Deploy your app to AWS", (yargs) => yargs
|
|
4
6
|
.option("from", {
|
|
5
7
|
type: "string",
|
|
@@ -17,6 +19,7 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
|
|
|
17
19
|
const { loadAssembly, Stacks } = await import("../../stacks/index.js");
|
|
18
20
|
const { render } = await import("ink");
|
|
19
21
|
const { DeploymentUI } = await import("../ui/deploy.js");
|
|
22
|
+
const { mapValues } = await import("remeda");
|
|
20
23
|
const project = useProject();
|
|
21
24
|
const identity = await useSTSIdentity();
|
|
22
25
|
Colors.line(`${Colors.primary.bold(`SST v${project.version}`)}`);
|
|
@@ -35,7 +38,7 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
|
|
|
35
38
|
return result;
|
|
36
39
|
}
|
|
37
40
|
const spinner = createSpinner({
|
|
38
|
-
text: " Building
|
|
41
|
+
text: " Building...",
|
|
39
42
|
});
|
|
40
43
|
const result = await Stacks.synth({
|
|
41
44
|
fn: project.stacks,
|
|
@@ -57,5 +60,6 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
|
|
|
57
60
|
printDeploymentResults(assembly, results);
|
|
58
61
|
if (Object.values(results).some((stack) => Stacks.isFailed(stack.status)))
|
|
59
62
|
process.exit(1);
|
|
63
|
+
fs.writeFile(path.join(project.paths.out, "outputs.json"), JSON.stringify(mapValues(results, (val) => val.outputs), null, 2));
|
|
60
64
|
process.exit(0);
|
|
61
65
|
});
|
package/cli/commands/dev.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
1
|
import { Colors } from "../colors.js";
|
|
3
2
|
import { printHeader } from "../ui/header.js";
|
|
3
|
+
import { mapValues, omitBy, pipe } from "remeda";
|
|
4
4
|
export const dev = (program) => program.command(["dev", "start"], "Work on your app locally", (yargs) => yargs.option("increase-timeout", {
|
|
5
5
|
type: "boolean",
|
|
6
6
|
description: "Increase function timeout",
|
|
@@ -69,11 +69,17 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
|
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
bus.subscribe("function.build.success", async (evt) => {
|
|
72
|
-
|
|
72
|
+
const info = useFunctions().fromID(evt.properties.functionID);
|
|
73
|
+
if (!info.enableLiveDev)
|
|
74
|
+
return;
|
|
75
|
+
Colors.line(Colors.dim(Colors.prefix, "Built", info.handler));
|
|
73
76
|
});
|
|
74
77
|
bus.subscribe("function.build.failed", async (evt) => {
|
|
78
|
+
const info = useFunctions().fromID(evt.properties.functionID);
|
|
79
|
+
if (!info.enableLiveDev)
|
|
80
|
+
return;
|
|
75
81
|
Colors.gap();
|
|
76
|
-
Colors.line(Colors.danger("✖ "), "Build failed",
|
|
82
|
+
Colors.line(Colors.danger("✖ "), "Build failed", info.handler);
|
|
77
83
|
for (const line of evt.properties.errors) {
|
|
78
84
|
Colors.line(" ", line);
|
|
79
85
|
}
|
|
@@ -81,9 +87,10 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
|
|
|
81
87
|
});
|
|
82
88
|
bus.subscribe("function.success", async (evt) => {
|
|
83
89
|
// stdout logs sometimes come in after
|
|
90
|
+
const p = prefix(evt.properties.requestID);
|
|
84
91
|
const req = pending.get(evt.properties.requestID);
|
|
85
92
|
setTimeout(() => {
|
|
86
|
-
Colors.line(
|
|
93
|
+
Colors.line(p, Colors.dim(`Done in ${Date.now() - req.started - 100}ms`));
|
|
87
94
|
end(evt.properties.requestID);
|
|
88
95
|
}, 100);
|
|
89
96
|
});
|
|
@@ -102,9 +109,15 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
|
|
|
102
109
|
const project = useProject();
|
|
103
110
|
const bus = useBus();
|
|
104
111
|
let lastDeployed;
|
|
105
|
-
let
|
|
106
|
-
let
|
|
112
|
+
let isWorking = false;
|
|
113
|
+
let isDirty = false;
|
|
107
114
|
async function build() {
|
|
115
|
+
if (isWorking) {
|
|
116
|
+
isDirty = true;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
isDirty = false;
|
|
120
|
+
isWorking = true;
|
|
108
121
|
Colors.gap();
|
|
109
122
|
const spinner = createSpinner({
|
|
110
123
|
color: "gray",
|
|
@@ -125,6 +138,9 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
|
|
|
125
138
|
Logger.debug("Checksum", "next", next, "old", lastDeployed);
|
|
126
139
|
if (next === lastDeployed) {
|
|
127
140
|
spinner.succeed(Colors.dim(" Built with no changes"));
|
|
141
|
+
isWorking = false;
|
|
142
|
+
if (isDirty)
|
|
143
|
+
build();
|
|
128
144
|
return;
|
|
129
145
|
}
|
|
130
146
|
if (!lastDeployed) {
|
|
@@ -134,11 +150,9 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
|
|
|
134
150
|
}
|
|
135
151
|
else {
|
|
136
152
|
spinner.succeed(Colors.dim(` Built`));
|
|
137
|
-
Colors.
|
|
153
|
+
Colors.gap();
|
|
138
154
|
}
|
|
139
|
-
|
|
140
|
-
if (lastDeployed)
|
|
141
|
-
setTimeout(() => deploy(), 100);
|
|
155
|
+
deploy(assembly);
|
|
142
156
|
}
|
|
143
157
|
catch (ex) {
|
|
144
158
|
spinner.fail();
|
|
@@ -149,17 +163,8 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
|
|
|
149
163
|
Colors.gap();
|
|
150
164
|
}
|
|
151
165
|
}
|
|
152
|
-
async function deploy() {
|
|
153
|
-
if (!pending)
|
|
154
|
-
return;
|
|
155
|
-
if (isDeploying)
|
|
156
|
-
return;
|
|
157
|
-
isDeploying = true;
|
|
158
|
-
const assembly = pending;
|
|
166
|
+
async function deploy(assembly) {
|
|
159
167
|
const nextChecksum = await checksum(assembly.directory);
|
|
160
|
-
pending = undefined;
|
|
161
|
-
if (lastDeployed)
|
|
162
|
-
console.log();
|
|
163
168
|
const component = render(React.createElement(DeploymentUI, { assembly: assembly }));
|
|
164
169
|
const results = await Stacks.deployMany(assembly.stacks);
|
|
165
170
|
component.clear();
|
|
@@ -181,8 +186,10 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
|
|
|
181
186
|
}
|
|
182
187
|
await SiteEnv.writeValues(result);
|
|
183
188
|
}
|
|
184
|
-
|
|
185
|
-
|
|
189
|
+
fs.writeFile(path.join(project.paths.out, "outputs.json"), JSON.stringify(pipe(results, omitBy((_, key) => key.includes("SstSiteEnv")), mapValues((val) => val.outputs)), null, 2));
|
|
190
|
+
isWorking = false;
|
|
191
|
+
if (isDirty)
|
|
192
|
+
build();
|
|
186
193
|
}
|
|
187
194
|
async function checksum(cdkOutPath) {
|
|
188
195
|
const manifestPath = path.join(cdkOutPath, "manifest.json");
|
|
@@ -209,11 +216,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
|
|
|
209
216
|
build();
|
|
210
217
|
});
|
|
211
218
|
await build();
|
|
212
|
-
await deploy();
|
|
213
219
|
});
|
|
214
|
-
const project = useProject();
|
|
215
|
-
const primary = chalk.hex("#E27152");
|
|
216
|
-
const link = chalk.cyan;
|
|
217
220
|
await useLocalServer({
|
|
218
221
|
key: "",
|
|
219
222
|
cert: "",
|
|
@@ -221,17 +224,6 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
|
|
|
221
224
|
});
|
|
222
225
|
clear();
|
|
223
226
|
await printHeader({ console: true, hint: "ready!" });
|
|
224
|
-
/*
|
|
225
|
-
console.log(` ${primary(`➜`)} ${bold(dim(`Outputs:`))}`);
|
|
226
|
-
for (let i = 0; i < 3; i++) {
|
|
227
|
-
console.log(` ${dim(`thdxr-scratch-MyStack`)}`);
|
|
228
|
-
console.log(
|
|
229
|
-
` ${bold(
|
|
230
|
-
dim(`ApiEndpoint`)
|
|
231
|
-
)}: https://hdq3z0es2d.execute-api.us-east-1.amazonaws.com`
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
*/
|
|
235
227
|
await Promise.all([
|
|
236
228
|
useRuntimeWorkers(),
|
|
237
229
|
useIOTBridge(),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="yargs" />
|
|
2
|
+
import type { Program } from "../program.js";
|
|
3
|
+
export declare const telemetry: (program: Program) => import("yargs").Argv<{
|
|
4
|
+
stage: string | undefined;
|
|
5
|
+
} & {
|
|
6
|
+
profile: string | undefined;
|
|
7
|
+
} & {
|
|
8
|
+
region: string | undefined;
|
|
9
|
+
} & {
|
|
10
|
+
verbose: boolean | undefined;
|
|
11
|
+
} & {
|
|
12
|
+
role: string | undefined;
|
|
13
|
+
} & {
|
|
14
|
+
action: string;
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Colors } from "../colors.js";
|
|
2
|
+
export const telemetry = (program) => program.command("telemetry <action>", "Load environment variables and start your frontend", (yargs) => yargs.positional("action", {
|
|
3
|
+
type: "string",
|
|
4
|
+
describe: "Whether to enable or disable",
|
|
5
|
+
choices: ["enable", "disable"],
|
|
6
|
+
demandOption: true,
|
|
7
|
+
}), async (args) => {
|
|
8
|
+
const { enable, disable } = await import("../telemetry/telemetry.js");
|
|
9
|
+
if (args.action === "enable") {
|
|
10
|
+
enable();
|
|
11
|
+
Colors.line(Colors.success(`✔ `), `Telemetry enabled`);
|
|
12
|
+
}
|
|
13
|
+
if (args.action === "disable") {
|
|
14
|
+
disable();
|
|
15
|
+
Colors.line(Colors.success(`✔ `), `Telemetry disabled`);
|
|
16
|
+
}
|
|
17
|
+
});
|
package/cli/program.js
CHANGED
|
@@ -22,7 +22,7 @@ export const program = yargs(hideBin(process.argv))
|
|
|
22
22
|
type: "string",
|
|
23
23
|
describe: "ARN of the IAM role to use when invoking AWS",
|
|
24
24
|
})
|
|
25
|
-
.group(["stage", "profile", "region", "
|
|
25
|
+
.group(["stage", "profile", "region", "role", "verbose", "help"], "Global:")
|
|
26
26
|
.middleware(async (argv) => {
|
|
27
27
|
if (argv.verbose) {
|
|
28
28
|
process.env.SST_VERBOSE = "1";
|
package/cli/sst.js
CHANGED
|
@@ -20,6 +20,7 @@ import { update } from "./commands/update.js";
|
|
|
20
20
|
import { transform } from "./commands/transform.js";
|
|
21
21
|
import { diff } from "./commands/diff.js";
|
|
22
22
|
import { version } from "./commands/version.js";
|
|
23
|
+
import { telemetry } from "./commands/telemetry.js";
|
|
23
24
|
dev(program);
|
|
24
25
|
deploy(program);
|
|
25
26
|
build(program);
|
|
@@ -32,6 +33,7 @@ transform(program);
|
|
|
32
33
|
consoleCommand(program);
|
|
33
34
|
diff(program);
|
|
34
35
|
version(program);
|
|
36
|
+
telemetry(program);
|
|
35
37
|
// @ts-expect-error
|
|
36
38
|
process.setSourceMapsEnabled(true);
|
|
37
39
|
process.removeAllListeners("uncaughtException");
|
package/cli/ui/deploy.js
CHANGED
|
@@ -70,15 +70,7 @@ export function printDeploymentResults(assembly, results, remove) {
|
|
|
70
70
|
Colors.gap();
|
|
71
71
|
Colors.line(Colors.success(`✔`), Colors.bold(remove ? ` Removed:` : ` Deployed:`));
|
|
72
72
|
for (const [stack, result] of success) {
|
|
73
|
-
const outputs = Object.entries(result.outputs).filter(([key, _]) =>
|
|
74
|
-
if (key.startsWith("Export"))
|
|
75
|
-
return false;
|
|
76
|
-
if (key.includes("SstSiteEnv"))
|
|
77
|
-
return false;
|
|
78
|
-
if (key === "SSTMetadata")
|
|
79
|
-
return false;
|
|
80
|
-
return true;
|
|
81
|
-
});
|
|
73
|
+
const outputs = Object.entries(result.outputs).filter(([key, _]) => !key.includes("SstSiteEnv"));
|
|
82
74
|
Colors.line(` ${Colors.dim(stackNameToId(stack))}`);
|
|
83
75
|
if (outputs.length > 0) {
|
|
84
76
|
for (const key of Object.keys(Object.fromEntries(outputs)).sort()) {
|
|
@@ -87,6 +79,7 @@ export function printDeploymentResults(assembly, results, remove) {
|
|
|
87
79
|
}
|
|
88
80
|
}
|
|
89
81
|
}
|
|
82
|
+
Colors.gap();
|
|
90
83
|
}
|
|
91
84
|
// Print failed stacks
|
|
92
85
|
const failed = Object.entries(results).filter(([_stack, result]) => Object.keys(result.errors).length > 0);
|
|
@@ -121,9 +114,9 @@ function logicalIdToCdkPath(assembly, stack, logicalId) {
|
|
|
121
114
|
}
|
|
122
115
|
function getHelper(error) {
|
|
123
116
|
return `This is a common deploy error. Check out this GitHub issue for more details - https://github.com/serverless-stack/sst/issues/125`;
|
|
124
|
-
return getApiAccessLogPermissionsHelper(error)
|
|
125
|
-
|
|
126
|
-
|
|
117
|
+
return (getApiAccessLogPermissionsHelper(error) ||
|
|
118
|
+
getAppSyncMultiResolverHelper(error) ||
|
|
119
|
+
getApiLogRoleHelper(error));
|
|
127
120
|
}
|
|
128
121
|
function getApiAccessLogPermissionsHelper(error) {
|
|
129
122
|
// Can run into this issue when enabling access logs for API Gateway
|
|
@@ -17,7 +17,6 @@ export declare class AstroSite extends SsrSite {
|
|
|
17
17
|
serverBuildOutputFile: string;
|
|
18
18
|
clientBuildOutputDir: string;
|
|
19
19
|
clientBuildVersionedSubDir: string;
|
|
20
|
-
siteStub: string;
|
|
21
20
|
};
|
|
22
21
|
protected validateBuildOutput(): void;
|
|
23
22
|
protected createFunctionForRegional(): lambda.Function;
|
package/constructs/AstroSite.js
CHANGED
|
@@ -4,6 +4,7 @@ import path from "path";
|
|
|
4
4
|
import * as esbuild from "esbuild";
|
|
5
5
|
import { SsrSite } from "./SsrSite.js";
|
|
6
6
|
import { Function } from "./Function.js";
|
|
7
|
+
import { useProject } from "../project.js";
|
|
7
8
|
import { EdgeFunction } from "./EdgeFunction.js";
|
|
8
9
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
|
9
10
|
/**
|
|
@@ -23,7 +24,6 @@ export class AstroSite extends SsrSite {
|
|
|
23
24
|
serverBuildOutputFile: "dist/server/entry.mjs",
|
|
24
25
|
clientBuildOutputDir: "dist/client",
|
|
25
26
|
clientBuildVersionedSubDir: "assets",
|
|
26
|
-
siteStub: path.resolve(__dirname, "../support/astro-site-html-stub"),
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
validateBuildOutput() {
|
|
@@ -36,14 +36,10 @@ export class AstroSite extends SsrSite {
|
|
|
36
36
|
}
|
|
37
37
|
createFunctionForRegional() {
|
|
38
38
|
const { defaults, environment, bind } = this.props;
|
|
39
|
-
// Bundle code
|
|
40
|
-
const handler = this.isPlaceholder
|
|
41
|
-
? path.resolve(__dirname, "../support/ssr-site-function-stub/index.handler")
|
|
42
|
-
: path.join(this.props.path, "dist", "server", "entry.handler");
|
|
43
39
|
// Create function
|
|
44
40
|
const fn = new Function(this, `ServerFunction`, {
|
|
45
41
|
description: "Server handler",
|
|
46
|
-
handler,
|
|
42
|
+
handler: path.join(this.props.path, "dist", "server", "entry.handler"),
|
|
47
43
|
bind,
|
|
48
44
|
logRetention: "three_days",
|
|
49
45
|
runtime: "nodejs16.x",
|
|
@@ -60,50 +56,39 @@ export class AstroSite extends SsrSite {
|
|
|
60
56
|
}
|
|
61
57
|
createFunctionForEdge() {
|
|
62
58
|
const { defaults, environment } = this.props;
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
`import { createRequire as topLevelCreateRequire } from 'module';`,
|
|
89
|
-
`const require = topLevelCreateRequire(import.meta.url);`,
|
|
90
|
-
].join(""),
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
if (result.errors.length > 0) {
|
|
94
|
-
result.errors.forEach((error) => console.error(error));
|
|
95
|
-
throw new Error(`There was a problem bundling the function code for the ${this.id} AstroSite.`);
|
|
96
|
-
}
|
|
97
|
-
// Create package.json
|
|
98
|
-
fs.writeFileSync(path.join(outputPath, "package.json"), `{"type":"module"}`);
|
|
99
|
-
bundlePath = outputPath;
|
|
100
|
-
handler = "entry.handler";
|
|
59
|
+
// Create a directory that we will use to create the bundled version
|
|
60
|
+
// of the "core server build" along with our custom Lamba server handler.
|
|
61
|
+
const outputPath = path.resolve(path.join(useProject().paths.artifacts, `AstroSiteFunction-${this.node.id}-${this.node.addr}`));
|
|
62
|
+
const result = esbuild.buildSync({
|
|
63
|
+
entryPoints: [
|
|
64
|
+
path.join(this.props.path, this.buildConfig.serverBuildOutputFile),
|
|
65
|
+
],
|
|
66
|
+
target: "esnext",
|
|
67
|
+
format: "esm",
|
|
68
|
+
platform: "node",
|
|
69
|
+
metafile: true,
|
|
70
|
+
bundle: true,
|
|
71
|
+
write: true,
|
|
72
|
+
allowOverwrite: true,
|
|
73
|
+
outfile: path.join(outputPath, "entry.mjs"),
|
|
74
|
+
banner: {
|
|
75
|
+
js: [
|
|
76
|
+
`import { createRequire as topLevelCreateRequire } from 'module';`,
|
|
77
|
+
`const require = topLevelCreateRequire(import.meta.url);`,
|
|
78
|
+
].join(""),
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
if (result.errors.length > 0) {
|
|
82
|
+
result.errors.forEach((error) => console.error(error));
|
|
83
|
+
throw new Error(`There was a problem bundling the function code for the ${this.id} AstroSite.`);
|
|
101
84
|
}
|
|
85
|
+
// Create package.json
|
|
86
|
+
fs.writeFileSync(path.join(outputPath, "package.json"), `{"type":"module"}`);
|
|
102
87
|
// Create function
|
|
103
88
|
return new EdgeFunction(this, `Server`, {
|
|
104
89
|
scopeOverride: this,
|
|
105
|
-
bundlePath,
|
|
106
|
-
handler,
|
|
90
|
+
bundlePath: outputPath,
|
|
91
|
+
handler: "entry.handler",
|
|
107
92
|
timeout: defaults?.function?.timeout,
|
|
108
93
|
memory: defaults?.function?.memorySize,
|
|
109
94
|
permissions: defaults?.function?.permissions,
|
package/constructs/Job.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as iam from "aws-cdk-lib/aws-iam";
|
|
|
7
7
|
import * as lambda from "aws-cdk-lib/aws-lambda";
|
|
8
8
|
import * as codebuild from "aws-cdk-lib/aws-codebuild";
|
|
9
9
|
import { Stack } from "./Stack.js";
|
|
10
|
-
import { Function } from "./Function.js";
|
|
10
|
+
import { Function, useFunctions } from "./Function.js";
|
|
11
11
|
import { toCdkDuration } from "./util/duration.js";
|
|
12
12
|
import { attachPermissionsToRole } from "./util/permission.js";
|
|
13
13
|
import { bindEnvironment, bindPermissions } from "./util/functionBinding.js";
|
|
@@ -44,6 +44,10 @@ export class Job extends Construct {
|
|
|
44
44
|
const app = this.node.root;
|
|
45
45
|
this.id = id;
|
|
46
46
|
this.props = props;
|
|
47
|
+
useFunctions().add(this.node.addr, {
|
|
48
|
+
...props,
|
|
49
|
+
runtime: "nodejs16.x",
|
|
50
|
+
});
|
|
47
51
|
this.localId = path.posix
|
|
48
52
|
.join(scope.node.path, id)
|
|
49
53
|
.replace(/\$/g, "-")
|
|
@@ -254,7 +258,7 @@ export class Job extends Construct {
|
|
|
254
258
|
}
|
|
255
259
|
createCodeBuildInvoker() {
|
|
256
260
|
return new Function(this, this.node.id, {
|
|
257
|
-
handler: path.join(__dirname, "../
|
|
261
|
+
handler: path.join(__dirname, "../support/job-invoker/index.main"),
|
|
258
262
|
runtime: "nodejs16.x",
|
|
259
263
|
timeout: 10,
|
|
260
264
|
memorySize: 1024,
|
|
@@ -21,13 +21,11 @@ export declare class NextjsSite extends SsrSite {
|
|
|
21
21
|
serverBuildOutputFile: string;
|
|
22
22
|
clientBuildOutputDir: string;
|
|
23
23
|
clientBuildVersionedSubDir: string;
|
|
24
|
-
siteStub: string;
|
|
25
24
|
};
|
|
26
25
|
protected createFunctionForRegional(): lambda.Function;
|
|
26
|
+
private createImageOptimizationFunctionForRegional;
|
|
27
|
+
private createMiddlewareEdgeFunctionForRegional;
|
|
27
28
|
protected createCloudFrontDistributionForRegional(): cloudfront.Distribution;
|
|
28
|
-
protected createCloudFrontDistributionForStub(): cloudfront.Distribution;
|
|
29
29
|
protected createCloudFrontServerCachePolicy(): cloudfront.CachePolicy;
|
|
30
30
|
protected generateBuildId(): string;
|
|
31
|
-
private createImageOptimizationFunctionForRegional;
|
|
32
|
-
private createMiddlewareEdgeFunctionForRegional;
|
|
33
31
|
}
|
package/constructs/NextjsSite.js
CHANGED
|
@@ -2,14 +2,14 @@ import fs from "fs";
|
|
|
2
2
|
import url from "url";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import spawn from "cross-spawn";
|
|
5
|
-
import { buildErrorResponsesForRedirectToIndex } from "./BaseSite.js";
|
|
6
5
|
import { Fn, Duration, RemovalPolicy, } from "aws-cdk-lib";
|
|
7
6
|
import * as logs from "aws-cdk-lib/aws-logs";
|
|
8
7
|
import * as lambda from "aws-cdk-lib/aws-lambda";
|
|
9
8
|
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
|
|
10
9
|
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";
|
|
11
|
-
import {
|
|
10
|
+
import { useProject } from "../project.js";
|
|
12
11
|
import { EdgeFunction } from "./EdgeFunction.js";
|
|
12
|
+
import { SsrSite } from "./SsrSite.js";
|
|
13
13
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
|
14
14
|
/**
|
|
15
15
|
* The `NextjsSite` construct is a higher level CDK construct that makes it easy to create a Next.js app.
|
|
@@ -34,50 +34,82 @@ export class NextjsSite extends SsrSite {
|
|
|
34
34
|
serverBuildOutputFile: ".open-next/server-function/index.mjs",
|
|
35
35
|
clientBuildOutputDir: ".open-next/assets",
|
|
36
36
|
clientBuildVersionedSubDir: "_next",
|
|
37
|
-
siteStub: path.resolve(__dirname, "../support/nextjs-site-html-stub"),
|
|
38
37
|
};
|
|
39
38
|
}
|
|
40
39
|
createFunctionForRegional() {
|
|
41
40
|
const { defaults, environment } = this.props;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
path.join(zipOutDir, "server-function.zip"),
|
|
59
|
-
], {
|
|
60
|
-
stdio: "inherit",
|
|
61
|
-
});
|
|
62
|
-
if (result.status !== 0) {
|
|
63
|
-
throw new Error(`There was a problem generating the assets package.`);
|
|
64
|
-
}
|
|
65
|
-
bundlePath = path.join(zipOutDir, "server-function.zip");
|
|
66
|
-
handler = "index.handler";
|
|
41
|
+
// Note: cannot point the bundlePath to the `.open-next/server-function`
|
|
42
|
+
// b/c the folder contains node_modules. And pnpm node_modules
|
|
43
|
+
// contains symlinks. CDK cannot zip symlinks correctly.
|
|
44
|
+
// https://github.com/aws/aws-cdk/issues/9251
|
|
45
|
+
// We will zip the folder ourselves.
|
|
46
|
+
const zipOutDir = path.resolve(path.join(useProject().paths.artifacts, `Site-${this.node.id}-${this.node.addr}`));
|
|
47
|
+
const script = path.resolve(__dirname, "../support/ssr-site-function-archiver.mjs");
|
|
48
|
+
const result = spawn.sync("node", [
|
|
49
|
+
script,
|
|
50
|
+
path.join(this.props.path, ".open-next", "server-function"),
|
|
51
|
+
path.join(zipOutDir, "server-function.zip"),
|
|
52
|
+
], {
|
|
53
|
+
stdio: "inherit",
|
|
54
|
+
});
|
|
55
|
+
if (result.status !== 0) {
|
|
56
|
+
throw new Error(`There was a problem generating the assets package.`);
|
|
67
57
|
}
|
|
68
58
|
return new lambda.Function(this, `ServerFunction`, {
|
|
69
59
|
description: "Server handler for Next.js",
|
|
70
|
-
handler,
|
|
60
|
+
handler: "index.handler",
|
|
61
|
+
currentVersionOptions: {
|
|
62
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
63
|
+
},
|
|
64
|
+
logRetention: logs.RetentionDays.THREE_DAYS,
|
|
65
|
+
code: lambda.Code.fromAsset(path.join(zipOutDir, "server-function.zip")),
|
|
66
|
+
runtime: lambda.Runtime.NODEJS_18_X,
|
|
67
|
+
memorySize: defaults?.function?.memorySize || 512,
|
|
68
|
+
timeout: Duration.seconds(defaults?.function?.timeout || 10),
|
|
69
|
+
environment,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
createImageOptimizationFunctionForRegional() {
|
|
73
|
+
const { defaults, path: sitePath } = this.props;
|
|
74
|
+
return new lambda.Function(this, `ImageFunction`, {
|
|
75
|
+
description: "Image optimization handler for Next.js",
|
|
76
|
+
handler: "index.handler",
|
|
71
77
|
currentVersionOptions: {
|
|
72
78
|
removalPolicy: RemovalPolicy.DESTROY,
|
|
73
79
|
},
|
|
74
80
|
logRetention: logs.RetentionDays.THREE_DAYS,
|
|
75
|
-
code: lambda.Code.fromAsset(
|
|
81
|
+
code: lambda.Code.fromAsset(path.join(sitePath, ".open-next/image-optimization-function")),
|
|
76
82
|
runtime: lambda.Runtime.NODEJS_18_X,
|
|
77
83
|
memorySize: defaults?.function?.memorySize || 512,
|
|
78
84
|
timeout: Duration.seconds(defaults?.function?.timeout || 10),
|
|
85
|
+
environment: {
|
|
86
|
+
BUCKET_NAME: this.cdk.bucket.bucketName,
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
createMiddlewareEdgeFunctionForRegional() {
|
|
91
|
+
const { defaults, environment, path: sitePath } = this.props;
|
|
92
|
+
const middlewarePath = path.resolve(sitePath, ".open-next/middleware-function");
|
|
93
|
+
const isMiddlewareEnabled = fs.existsSync(middlewarePath);
|
|
94
|
+
let bundlePath, handler;
|
|
95
|
+
if (isMiddlewareEnabled) {
|
|
96
|
+
bundlePath = middlewarePath;
|
|
97
|
+
handler = "index.handler";
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
bundlePath = path.resolve(__dirname, "../support/ssr-site-function-stub");
|
|
101
|
+
handler = "server.handler";
|
|
102
|
+
}
|
|
103
|
+
const fn = new EdgeFunction(this, "Middleware", {
|
|
104
|
+
bundlePath,
|
|
105
|
+
handler,
|
|
106
|
+
timeout: 5,
|
|
107
|
+
memory: 128,
|
|
108
|
+
permissions: defaults?.function?.permissions,
|
|
79
109
|
environment,
|
|
110
|
+
format: "esm",
|
|
80
111
|
});
|
|
112
|
+
return { fn, isMiddlewareEnabled };
|
|
81
113
|
}
|
|
82
114
|
createCloudFrontDistributionForRegional() {
|
|
83
115
|
const { cdk } = this.props;
|
|
@@ -96,7 +128,7 @@ export class NextjsSite extends SsrSite {
|
|
|
96
128
|
compress: true,
|
|
97
129
|
cachePolicy: cdk?.cachePolicies?.serverRequests ??
|
|
98
130
|
this.createCloudFrontServerCachePolicy(),
|
|
99
|
-
edgeLambdas: isMiddlewareEnabled
|
|
131
|
+
edgeLambdas: isMiddlewareEnabled
|
|
100
132
|
? [{
|
|
101
133
|
eventType: cloudfront.LambdaEdgeEventType.VIEWER_REQUEST,
|
|
102
134
|
functionVersion: middlewareFn.currentVersion,
|
|
@@ -208,22 +240,6 @@ export class NextjsSite extends SsrSite {
|
|
|
208
240
|
},
|
|
209
241
|
});
|
|
210
242
|
}
|
|
211
|
-
createCloudFrontDistributionForStub() {
|
|
212
|
-
// Keep creating middleware edge function b/c edge function cannot be removed
|
|
213
|
-
// immediately.
|
|
214
|
-
this.createMiddlewareEdgeFunctionForRegional();
|
|
215
|
-
// Create placeholder distribution
|
|
216
|
-
return new cloudfront.Distribution(this, "Distribution", {
|
|
217
|
-
defaultRootObject: "index.html",
|
|
218
|
-
errorResponses: buildErrorResponsesForRedirectToIndex("index.html"),
|
|
219
|
-
domainNames: this.buildDistributionDomainNames(),
|
|
220
|
-
certificate: this.cdk.certificate,
|
|
221
|
-
defaultBehavior: {
|
|
222
|
-
origin: new origins.S3Origin(this.cdk.bucket),
|
|
223
|
-
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
243
|
createCloudFrontServerCachePolicy() {
|
|
228
244
|
return new cloudfront.CachePolicy(this, "ServerCache", {
|
|
229
245
|
queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(),
|
|
@@ -242,61 +258,7 @@ export class NextjsSite extends SsrSite {
|
|
|
242
258
|
});
|
|
243
259
|
}
|
|
244
260
|
generateBuildId() {
|
|
245
|
-
if (this.isPlaceholder) {
|
|
246
|
-
return "live";
|
|
247
|
-
}
|
|
248
261
|
const filePath = path.join(this.props.path, ".next/BUILD_ID");
|
|
249
262
|
return fs.readFileSync(filePath).toString();
|
|
250
263
|
}
|
|
251
|
-
createImageOptimizationFunctionForRegional() {
|
|
252
|
-
const { defaults, path: sitePath } = this.props;
|
|
253
|
-
let bundlePath, handler;
|
|
254
|
-
if (this.isPlaceholder) {
|
|
255
|
-
bundlePath = path.resolve(__dirname, "../support/ssr-site-function-stub");
|
|
256
|
-
handler = "server.handler";
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
bundlePath = path.join(sitePath, ".open-next/image-optimization-function");
|
|
260
|
-
handler = "index.handler";
|
|
261
|
-
}
|
|
262
|
-
return new lambda.Function(this, `ImageFunction`, {
|
|
263
|
-
description: "Image optimization handler for Next.js",
|
|
264
|
-
handler,
|
|
265
|
-
currentVersionOptions: {
|
|
266
|
-
removalPolicy: RemovalPolicy.DESTROY,
|
|
267
|
-
},
|
|
268
|
-
logRetention: logs.RetentionDays.THREE_DAYS,
|
|
269
|
-
code: lambda.Code.fromAsset(bundlePath),
|
|
270
|
-
runtime: lambda.Runtime.NODEJS_18_X,
|
|
271
|
-
memorySize: defaults?.function?.memorySize || 512,
|
|
272
|
-
timeout: Duration.seconds(defaults?.function?.timeout || 10),
|
|
273
|
-
environment: {
|
|
274
|
-
BUCKET_NAME: this.cdk.bucket.bucketName,
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
createMiddlewareEdgeFunctionForRegional() {
|
|
279
|
-
const { defaults, environment, path: sitePath } = this.props;
|
|
280
|
-
const middlewarePath = path.resolve(sitePath, ".open-next/middleware-function");
|
|
281
|
-
const isMiddlewareEnabled = fs.existsSync(middlewarePath);
|
|
282
|
-
let bundlePath, handler;
|
|
283
|
-
if (this.isPlaceholder || !isMiddlewareEnabled) {
|
|
284
|
-
bundlePath = path.resolve(__dirname, "../support/ssr-site-function-stub");
|
|
285
|
-
handler = "server.handler";
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
bundlePath = middlewarePath;
|
|
289
|
-
handler = "index.handler";
|
|
290
|
-
}
|
|
291
|
-
const fn = new EdgeFunction(this, "Middleware", {
|
|
292
|
-
bundlePath,
|
|
293
|
-
handler,
|
|
294
|
-
timeout: 5,
|
|
295
|
-
memory: 128,
|
|
296
|
-
permissions: defaults?.function?.permissions,
|
|
297
|
-
environment,
|
|
298
|
-
format: "esm",
|
|
299
|
-
});
|
|
300
|
-
return { fn, isMiddlewareEnabled };
|
|
301
|
-
}
|
|
302
264
|
}
|