sst 2.31.0 → 2.32.0
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/constructs/App.js +7 -7
- package/constructs/Distribution.d.ts +5 -10
- package/constructs/Distribution.js +6 -4
- package/constructs/Function.d.ts +6 -6
- package/constructs/Function.js +3 -3
- package/constructs/NextjsSite.d.ts +13 -1
- package/constructs/NextjsSite.js +196 -35
- package/constructs/Service.d.ts +0 -1
- package/constructs/Service.js +0 -1
- package/constructs/SsrSite.d.ts +40 -16
- package/constructs/SsrSite.js +56 -46
- package/constructs/StaticSite.js +3 -2
- package/package.json +2 -2
- package/support/custom-resources/index.mjs +8510 -8439
package/constructs/App.js
CHANGED
|
@@ -264,28 +264,28 @@ export class App extends CDKApp {
|
|
|
264
264
|
const functions = useFunctions();
|
|
265
265
|
const sourcemaps = functions.sourcemaps.forStack(child.stackName);
|
|
266
266
|
if (sourcemaps.length) {
|
|
267
|
-
const policy = new Policy(child, "
|
|
267
|
+
const policy = new Policy(child, "SourcemapUploaderPolicy", {
|
|
268
268
|
statements: [
|
|
269
269
|
new PolicyStatement({
|
|
270
270
|
effect: Effect.ALLOW,
|
|
271
271
|
actions: ["s3:GetObject", "s3:PutObject"],
|
|
272
272
|
resources: [
|
|
273
|
-
sourcemaps[0].
|
|
273
|
+
sourcemaps[0].srcBucket.bucketArn + "/*",
|
|
274
274
|
`arn:${child.partition}:s3:::${bootstrap.bucket}/*`,
|
|
275
275
|
],
|
|
276
276
|
}),
|
|
277
277
|
],
|
|
278
278
|
});
|
|
279
279
|
child.customResourceHandler.role?.attachInlinePolicy(policy);
|
|
280
|
-
const resource = new CustomResource(child, "
|
|
280
|
+
const resource = new CustomResource(child, "SourcemapUploader", {
|
|
281
281
|
serviceToken: child.customResourceHandler.functionArn,
|
|
282
|
-
resourceType: "Custom::
|
|
282
|
+
resourceType: "Custom::SourcemapUploader",
|
|
283
283
|
properties: {
|
|
284
284
|
app: this.name,
|
|
285
285
|
stage: this.stage,
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
286
|
+
tarBucket: bootstrap.bucket,
|
|
287
|
+
srcBucket: sourcemaps[0].srcBucket.bucketName,
|
|
288
|
+
sourcemaps: sourcemaps.map((s) => [s.tarKey, s.srcKey]),
|
|
289
289
|
},
|
|
290
290
|
});
|
|
291
291
|
resource.node.addDependency(policy);
|
|
@@ -94,15 +94,6 @@ export interface DistributionProps {
|
|
|
94
94
|
* ```
|
|
95
95
|
*/
|
|
96
96
|
customDomain?: string | DistributionDomainProps;
|
|
97
|
-
/**
|
|
98
|
-
* The SSR function is deployed to Lambda in a single region. Alternatively, you can enable this option to deploy to Lambda@Edge.
|
|
99
|
-
* @default false
|
|
100
|
-
*/
|
|
101
|
-
/**
|
|
102
|
-
* While deploying, SST waits for the CloudFront cache invalidation process to finish. This ensures that the new content will be served once the deploy command finishes. However, this process can sometimes take more than 5 mins. For non-prod environments it might make sense to pass in `false`. That'll skip waiting for the cache to invalidate and speed up the deploy process.
|
|
103
|
-
* @default false
|
|
104
|
-
*/
|
|
105
|
-
waitForInvalidation?: boolean;
|
|
106
97
|
scopeOverride?: IConstruct;
|
|
107
98
|
cdk: {
|
|
108
99
|
distribution: CdkDistributionProps | IDistribution;
|
|
@@ -132,7 +123,11 @@ export declare class Distribution extends Construct {
|
|
|
132
123
|
hostedZone: IHostedZone | undefined;
|
|
133
124
|
certificate: ICertificate | undefined;
|
|
134
125
|
};
|
|
135
|
-
createInvalidation(
|
|
126
|
+
createInvalidation(props?: {
|
|
127
|
+
version?: string;
|
|
128
|
+
paths?: string[];
|
|
129
|
+
wait?: boolean;
|
|
130
|
+
}): CustomResource;
|
|
136
131
|
private validateCloudFrontDistributionSettings;
|
|
137
132
|
private validateCustomDomainSettings;
|
|
138
133
|
private lookupHostedZone;
|
|
@@ -70,7 +70,8 @@ export class Distribution extends Construct {
|
|
|
70
70
|
certificate: this.certificate,
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
|
-
createInvalidation(
|
|
73
|
+
createInvalidation(props) {
|
|
74
|
+
const { version, paths, wait } = props ?? {};
|
|
74
75
|
const stack = Stack.of(this);
|
|
75
76
|
const policy = new Policy(this.scope, "CloudFrontInvalidatorPolicy", {
|
|
76
77
|
statements: [
|
|
@@ -91,10 +92,11 @@ export class Distribution extends Construct {
|
|
|
91
92
|
serviceToken: stack.customResourceHandler.functionArn,
|
|
92
93
|
resourceType: "Custom::CloudFrontInvalidator",
|
|
93
94
|
properties: {
|
|
94
|
-
|
|
95
|
+
version: version ||
|
|
96
|
+
Date.now().toString(16) + Math.random().toString(16).slice(2),
|
|
95
97
|
distributionId: this.distribution.distributionId,
|
|
96
|
-
paths: paths ?? ["/*"],
|
|
97
|
-
|
|
98
|
+
paths: [...new Set(paths ?? ["/*"])],
|
|
99
|
+
wait: wait ?? false,
|
|
98
100
|
},
|
|
99
101
|
});
|
|
100
102
|
resource.node.addDependency(policy);
|
package/constructs/Function.d.ts
CHANGED
|
@@ -693,14 +693,14 @@ export declare class Function extends CDKFunction implements SSTConstruct {
|
|
|
693
693
|
export declare const useFunctions: () => {
|
|
694
694
|
sourcemaps: {
|
|
695
695
|
add(stack: string, source: {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
696
|
+
srcBucket: IBucket;
|
|
697
|
+
srcKey: string;
|
|
698
|
+
tarKey: string;
|
|
699
699
|
}): void;
|
|
700
700
|
forStack(stack: string): {
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
701
|
+
srcBucket: IBucket;
|
|
702
|
+
srcKey: string;
|
|
703
|
+
tarKey: string;
|
|
704
704
|
}[];
|
|
705
705
|
};
|
|
706
706
|
fromID(id: string): FunctionProps | undefined;
|
package/constructs/Function.js
CHANGED
|
@@ -257,9 +257,9 @@ export class Function extends CDKFunction {
|
|
|
257
257
|
});
|
|
258
258
|
await fs.rm(result.sourcemap);
|
|
259
259
|
useFunctions().sourcemaps.add(stack.stackName, {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
srcBucket: asset.bucket,
|
|
261
|
+
srcKey: asset.s3ObjectKey,
|
|
262
|
+
tarKey: this.functionArn,
|
|
263
263
|
});
|
|
264
264
|
}
|
|
265
265
|
// Update code
|
|
@@ -123,7 +123,11 @@ type NextjsSiteNormalizedProps = NextjsSiteProps & SsrSiteNormalizedProps;
|
|
|
123
123
|
*/
|
|
124
124
|
export declare class NextjsSite extends SsrSite {
|
|
125
125
|
props: NextjsSiteNormalizedProps;
|
|
126
|
-
private
|
|
126
|
+
private _routes?;
|
|
127
|
+
private routesManifest?;
|
|
128
|
+
private appPathRoutesManifest?;
|
|
129
|
+
private appPathsManifest?;
|
|
130
|
+
private pagesManifest?;
|
|
127
131
|
constructor(scope: Construct, id: string, props?: NextjsSiteProps);
|
|
128
132
|
static buildDefaultServerCachePolicyProps(): CachePolicyProps;
|
|
129
133
|
protected plan(bucket: Bucket): {
|
|
@@ -335,10 +339,18 @@ export declare class NextjsSite extends SsrSite {
|
|
|
335
339
|
};
|
|
336
340
|
};
|
|
337
341
|
private wrapServerFunction;
|
|
342
|
+
private removeSourcemaps;
|
|
338
343
|
private useRoutes;
|
|
344
|
+
private useRoutesManifest;
|
|
345
|
+
private useAppPathRoutesManifest;
|
|
346
|
+
private useAppPathsManifest;
|
|
347
|
+
private usePagesManifest;
|
|
339
348
|
private getBuildId;
|
|
349
|
+
private getSourcemapForAppRoute;
|
|
350
|
+
private getSourcemapForPagesRoute;
|
|
340
351
|
private isPerRouteLoggingEnabled;
|
|
341
352
|
private disableDefaultLogging;
|
|
353
|
+
private uploadSourcemaps;
|
|
342
354
|
private static buildCloudWatchRouteName;
|
|
343
355
|
private static buildCloudWatchRouteHash;
|
|
344
356
|
static _test: {
|
package/constructs/NextjsSite.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
+
import zlib from "zlib";
|
|
3
4
|
import crypto from "crypto";
|
|
5
|
+
import { globSync } from "glob";
|
|
4
6
|
import { Duration as CdkDuration, RemovalPolicy, CustomResource, } from "aws-cdk-lib/core";
|
|
5
7
|
import { Code, Runtime, Function as CdkFunction, Architecture, LayerVersion, } from "aws-cdk-lib/aws-lambda";
|
|
6
8
|
import { AttributeType, Billing, TableV2 as Table, } from "aws-cdk-lib/aws-dynamodb";
|
|
@@ -13,6 +15,9 @@ import { toCdkSize } from "./util/size.js";
|
|
|
13
15
|
import { Effect, Policy, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
14
16
|
import { RetentionDays } from "aws-cdk-lib/aws-logs";
|
|
15
17
|
import { VisibleError } from "../error.js";
|
|
18
|
+
import { Asset } from "aws-cdk-lib/aws-s3-assets";
|
|
19
|
+
import { useFunctions } from "./Function.js";
|
|
20
|
+
import { useDeferredTasks } from "./deferred_task.js";
|
|
16
21
|
const LAYER_VERSION = "2";
|
|
17
22
|
const DEFAULT_OPEN_NEXT_VERSION = "2.2.4";
|
|
18
23
|
const DEFAULT_CACHE_POLICY_ALLOWED_HEADERS = [
|
|
@@ -34,7 +39,11 @@ const DEFAULT_CACHE_POLICY_ALLOWED_HEADERS = [
|
|
|
34
39
|
* ```
|
|
35
40
|
*/
|
|
36
41
|
export class NextjsSite extends SsrSite {
|
|
37
|
-
|
|
42
|
+
_routes;
|
|
43
|
+
routesManifest;
|
|
44
|
+
appPathRoutesManifest;
|
|
45
|
+
appPathsManifest;
|
|
46
|
+
pagesManifest;
|
|
38
47
|
constructor(scope, id, props) {
|
|
39
48
|
const streaming = props?.experimental?.streaming ?? false;
|
|
40
49
|
const disableDynamoDBCache = props?.experimental?.disableDynamoDBCache ?? false;
|
|
@@ -57,6 +66,7 @@ export class NextjsSite extends SsrSite {
|
|
|
57
66
|
});
|
|
58
67
|
if (this.isPerRouteLoggingEnabled()) {
|
|
59
68
|
this.disableDefaultLogging();
|
|
69
|
+
this.uploadSourcemaps();
|
|
60
70
|
}
|
|
61
71
|
if (!disableIncrementalCache) {
|
|
62
72
|
this.createRevalidationQueue();
|
|
@@ -80,6 +90,7 @@ export class NextjsSite extends SsrSite {
|
|
|
80
90
|
CACHE_BUCKET_REGION: Stack.of(this).region,
|
|
81
91
|
},
|
|
82
92
|
});
|
|
93
|
+
this.removeSourcemaps();
|
|
83
94
|
return this.validatePlan({
|
|
84
95
|
cloudFrontFunctions: {
|
|
85
96
|
serverCfFunction: {
|
|
@@ -312,7 +323,10 @@ export class NextjsSite extends SsrSite {
|
|
|
312
323
|
const injections = [];
|
|
313
324
|
if (this.isPerRouteLoggingEnabled()) {
|
|
314
325
|
injections.push(`
|
|
315
|
-
const routeData = ${JSON.stringify(this.useRoutes()
|
|
326
|
+
const routeData = ${JSON.stringify(this.useRoutes().map(({ regex, logGroupPath }) => ({
|
|
327
|
+
regex,
|
|
328
|
+
logGroupPath,
|
|
329
|
+
})))}.find(({ regex }) => event.rawPath.match(new RegExp(regex)));
|
|
316
330
|
if (routeData) {
|
|
317
331
|
console.log("::sst::" + JSON.stringify({
|
|
318
332
|
action:"log.split",
|
|
@@ -349,53 +363,178 @@ export class NextjsSite extends SsrSite {
|
|
|
349
363
|
handler: `${wrapperName}.handler`,
|
|
350
364
|
};
|
|
351
365
|
}
|
|
366
|
+
removeSourcemaps() {
|
|
367
|
+
const { path: sitePath } = this.props;
|
|
368
|
+
const files = globSync("**/*.js.map", {
|
|
369
|
+
cwd: path.join(sitePath, ".open-next", "server-function"),
|
|
370
|
+
nodir: true,
|
|
371
|
+
dot: true,
|
|
372
|
+
});
|
|
373
|
+
for (const file of files) {
|
|
374
|
+
fs.rmSync(path.join(sitePath, ".open-next", "server-function", file));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
352
377
|
useRoutes() {
|
|
353
|
-
if (this.
|
|
354
|
-
return this.
|
|
355
|
-
const
|
|
378
|
+
if (this._routes)
|
|
379
|
+
return this._routes;
|
|
380
|
+
const routesManifest = this.useRoutesManifest();
|
|
381
|
+
this._routes = [
|
|
382
|
+
...[...routesManifest.dynamicRoutes, ...routesManifest.staticRoutes]
|
|
383
|
+
.map(({ page, regex }) => {
|
|
384
|
+
const cwRoute = NextjsSite.buildCloudWatchRouteName(page);
|
|
385
|
+
const cwHash = NextjsSite.buildCloudWatchRouteHash(page);
|
|
386
|
+
const sourcemapPath = this.getSourcemapForAppRoute(page) ||
|
|
387
|
+
this.getSourcemapForPagesRoute(page);
|
|
388
|
+
return {
|
|
389
|
+
route: page,
|
|
390
|
+
regex,
|
|
391
|
+
logGroupPath: `/${cwHash}${cwRoute}`,
|
|
392
|
+
sourcemapPath: sourcemapPath,
|
|
393
|
+
sourcemapKey: cwHash,
|
|
394
|
+
};
|
|
395
|
+
})
|
|
396
|
+
.sort((a, b) => a.route.localeCompare(b.route)),
|
|
397
|
+
...(routesManifest.dataRoutes || [])
|
|
398
|
+
.map(({ page, dataRouteRegex }) => {
|
|
399
|
+
const routeDisplayName = page.endsWith("/")
|
|
400
|
+
? `/_next/data/BUILD_ID${page}index.json`
|
|
401
|
+
: `/_next/data/BUILD_ID${page}.json`;
|
|
402
|
+
const cwRoute = NextjsSite.buildCloudWatchRouteName(routeDisplayName);
|
|
403
|
+
const cwHash = NextjsSite.buildCloudWatchRouteHash(page);
|
|
404
|
+
return {
|
|
405
|
+
route: routeDisplayName,
|
|
406
|
+
regex: dataRouteRegex,
|
|
407
|
+
logGroupPath: `/${cwHash}${cwRoute}`,
|
|
408
|
+
};
|
|
409
|
+
})
|
|
410
|
+
.sort((a, b) => a.route.localeCompare(b.route)),
|
|
411
|
+
];
|
|
412
|
+
return this._routes;
|
|
413
|
+
}
|
|
414
|
+
useRoutesManifest() {
|
|
415
|
+
if (this.routesManifest)
|
|
416
|
+
return this.routesManifest;
|
|
356
417
|
const { path: sitePath } = this.props;
|
|
418
|
+
const id = this.node.id;
|
|
357
419
|
try {
|
|
358
|
-
const content =
|
|
420
|
+
const content = fs
|
|
359
421
|
.readFileSync(path.join(sitePath, ".next/routes-manifest.json"))
|
|
360
|
-
.toString()
|
|
361
|
-
this.
|
|
362
|
-
|
|
363
|
-
.map(({ page, regex }) => {
|
|
364
|
-
const cwRoute = NextjsSite.buildCloudWatchRouteName(page);
|
|
365
|
-
const cwHash = NextjsSite.buildCloudWatchRouteHash(page);
|
|
366
|
-
return {
|
|
367
|
-
route: page,
|
|
368
|
-
regex,
|
|
369
|
-
logGroupPath: `/${cwHash}${cwRoute}`,
|
|
370
|
-
};
|
|
371
|
-
})
|
|
372
|
-
.sort((a, b) => a.route.localeCompare(b.route)),
|
|
373
|
-
...(content.dataRoutes || [])
|
|
374
|
-
.map(({ page, dataRouteRegex }) => {
|
|
375
|
-
const routeDisplayName = page.endsWith("/")
|
|
376
|
-
? `/_next/data/BUILD_ID${page}index.json`
|
|
377
|
-
: `/_next/data/BUILD_ID${page}.json`;
|
|
378
|
-
const cwRoute = NextjsSite.buildCloudWatchRouteName(routeDisplayName);
|
|
379
|
-
const cwHash = NextjsSite.buildCloudWatchRouteHash(`data:${page}`);
|
|
380
|
-
return {
|
|
381
|
-
route: routeDisplayName,
|
|
382
|
-
regex: dataRouteRegex,
|
|
383
|
-
logGroupPath: `/${cwHash}${cwRoute}`,
|
|
384
|
-
};
|
|
385
|
-
})
|
|
386
|
-
.sort((a, b) => a.route.localeCompare(b.route)),
|
|
387
|
-
];
|
|
388
|
-
return this.routes;
|
|
422
|
+
.toString();
|
|
423
|
+
this.routesManifest = JSON.parse(content);
|
|
424
|
+
return this.routesManifest;
|
|
389
425
|
}
|
|
390
426
|
catch (e) {
|
|
391
427
|
console.error(e);
|
|
392
428
|
throw new VisibleError(`Failed to read routes data from ".next/routes-manifest.json" for the "${id}" site.`);
|
|
393
429
|
}
|
|
394
430
|
}
|
|
431
|
+
useAppPathRoutesManifest() {
|
|
432
|
+
if (this.appPathRoutesManifest)
|
|
433
|
+
return this.appPathRoutesManifest;
|
|
434
|
+
const { path: sitePath } = this.props;
|
|
435
|
+
try {
|
|
436
|
+
const content = fs
|
|
437
|
+
.readFileSync(path.join(sitePath, ".next/app-path-routes-manifest.json"))
|
|
438
|
+
.toString();
|
|
439
|
+
this.appPathRoutesManifest = JSON.parse(content);
|
|
440
|
+
return this.appPathRoutesManifest;
|
|
441
|
+
}
|
|
442
|
+
catch (e) {
|
|
443
|
+
return {};
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
useAppPathsManifest() {
|
|
447
|
+
if (this.appPathsManifest)
|
|
448
|
+
return this.appPathsManifest;
|
|
449
|
+
const { path: sitePath } = this.props;
|
|
450
|
+
try {
|
|
451
|
+
const content = fs
|
|
452
|
+
.readFileSync(path.join(sitePath, ".next/server/app-paths-manifest.json"))
|
|
453
|
+
.toString();
|
|
454
|
+
this.appPathsManifest = JSON.parse(content);
|
|
455
|
+
return this.appPathsManifest;
|
|
456
|
+
}
|
|
457
|
+
catch (e) {
|
|
458
|
+
return {};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
usePagesManifest() {
|
|
462
|
+
if (this.pagesManifest)
|
|
463
|
+
return this.pagesManifest;
|
|
464
|
+
const { path: sitePath } = this.props;
|
|
465
|
+
try {
|
|
466
|
+
const content = fs
|
|
467
|
+
.readFileSync(path.join(sitePath, ".next/server/pages-manifest.json"))
|
|
468
|
+
.toString();
|
|
469
|
+
this.pagesManifest = JSON.parse(content);
|
|
470
|
+
return this.pagesManifest;
|
|
471
|
+
}
|
|
472
|
+
catch (e) {
|
|
473
|
+
return {};
|
|
474
|
+
}
|
|
475
|
+
}
|
|
395
476
|
getBuildId() {
|
|
396
477
|
const { path: sitePath } = this.props;
|
|
397
478
|
return fs.readFileSync(path.join(sitePath, ".next/BUILD_ID")).toString();
|
|
398
479
|
}
|
|
480
|
+
getSourcemapForAppRoute(page) {
|
|
481
|
+
const { path: sitePath } = this.props;
|
|
482
|
+
// Step 1: look up in "appPathRoutesManifest" to find the key with
|
|
483
|
+
// value equal to the page
|
|
484
|
+
// {
|
|
485
|
+
// "/_not-found": "/_not-found",
|
|
486
|
+
// "/about/page": "/about",
|
|
487
|
+
// "/about/profile/page": "/about/profile",
|
|
488
|
+
// "/page": "/",
|
|
489
|
+
// "/favicon.ico/route": "/favicon.ico"
|
|
490
|
+
// }
|
|
491
|
+
const appPathRoutesManifest = this.useAppPathRoutesManifest();
|
|
492
|
+
const appPathRoute = Object.keys(appPathRoutesManifest).find((key) => appPathRoutesManifest[key] === page);
|
|
493
|
+
if (!appPathRoute)
|
|
494
|
+
return;
|
|
495
|
+
// Step 2: look up in "appPathsManifest" to find the file with key equal
|
|
496
|
+
// to the page
|
|
497
|
+
// {
|
|
498
|
+
// "/_not-found": "app/_not-found.js",
|
|
499
|
+
// "/about/page": "app/about/page.js",
|
|
500
|
+
// "/about/profile/page": "app/about/profile/page.js",
|
|
501
|
+
// "/page": "app/page.js",
|
|
502
|
+
// "/favicon.ico/route": "app/favicon.ico/route.js"
|
|
503
|
+
// }
|
|
504
|
+
const appPathsManifest = this.useAppPathsManifest();
|
|
505
|
+
const filePath = appPathsManifest[appPathRoute];
|
|
506
|
+
if (!filePath)
|
|
507
|
+
return;
|
|
508
|
+
// Step 3: check the .map file exists
|
|
509
|
+
const sourcemapPath = path.join(sitePath, ".next", "server", `${filePath}.map`);
|
|
510
|
+
if (!fs.existsSync(sourcemapPath))
|
|
511
|
+
return;
|
|
512
|
+
return sourcemapPath;
|
|
513
|
+
}
|
|
514
|
+
getSourcemapForPagesRoute(page) {
|
|
515
|
+
const { path: sitePath } = this.props;
|
|
516
|
+
// Step 1: look up in "pathsManifest" to find the file with key equal
|
|
517
|
+
// to the page
|
|
518
|
+
// {
|
|
519
|
+
// "/_app": "pages/_app.js",
|
|
520
|
+
// "/_error": "pages/_error.js",
|
|
521
|
+
// "/404": "pages/404.html",
|
|
522
|
+
// "/api/hello": "pages/api/hello.js",
|
|
523
|
+
// "/api/auth/[...nextauth]": "pages/api/auth/[...nextauth].js",
|
|
524
|
+
// "/api/next-auth-restricted": "pages/api/next-auth-restricted.js",
|
|
525
|
+
// "/": "pages/index.js",
|
|
526
|
+
// "/ssr": "pages/ssr.js"
|
|
527
|
+
// }
|
|
528
|
+
const pagesManifest = this.usePagesManifest();
|
|
529
|
+
const filePath = pagesManifest[page];
|
|
530
|
+
if (!filePath)
|
|
531
|
+
return;
|
|
532
|
+
// Step 2: check the .map file exists
|
|
533
|
+
const sourcemapPath = path.join(sitePath, ".next", "server", `${filePath}.map`);
|
|
534
|
+
if (!fs.existsSync(sourcemapPath))
|
|
535
|
+
return;
|
|
536
|
+
return sourcemapPath;
|
|
537
|
+
}
|
|
399
538
|
isPerRouteLoggingEnabled() {
|
|
400
539
|
return (!this.doNotDeploy &&
|
|
401
540
|
!this.props.edge &&
|
|
@@ -424,6 +563,28 @@ export class NextjsSite extends SsrSite {
|
|
|
424
563
|
});
|
|
425
564
|
server.role?.attachInlinePolicy(policy);
|
|
426
565
|
}
|
|
566
|
+
uploadSourcemaps() {
|
|
567
|
+
const stack = Stack.of(this);
|
|
568
|
+
const server = this.serverFunction;
|
|
569
|
+
this.useRoutes().forEach(({ sourcemapPath, sourcemapKey }) => {
|
|
570
|
+
if (!sourcemapPath || !sourcemapKey)
|
|
571
|
+
return;
|
|
572
|
+
useDeferredTasks().add(async () => {
|
|
573
|
+
// zip sourcemap
|
|
574
|
+
const zipPath = `${sourcemapPath}.gz.zip`;
|
|
575
|
+
const data = await fs.promises.readFile(sourcemapPath);
|
|
576
|
+
await fs.promises.writeFile(zipPath, zlib.gzipSync(data));
|
|
577
|
+
const asset = new Asset(this, `Sourcemap-${sourcemapKey}`, {
|
|
578
|
+
path: zipPath,
|
|
579
|
+
});
|
|
580
|
+
useFunctions().sourcemaps.add(stack.stackName, {
|
|
581
|
+
srcBucket: asset.bucket,
|
|
582
|
+
srcKey: asset.s3ObjectKey,
|
|
583
|
+
tarKey: path.join(server.functionArn, sourcemapKey),
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
}
|
|
427
588
|
static buildCloudWatchRouteName(route) {
|
|
428
589
|
return route.replace(/[^a-zA-Z0-9_\-/.#]/g, "");
|
|
429
590
|
}
|
package/constructs/Service.d.ts
CHANGED
|
@@ -375,7 +375,6 @@ type ServiceNormalizedProps = ServiceProps & {
|
|
|
375
375
|
memory: Exclude<ServiceProps["memory"], undefined>;
|
|
376
376
|
port: Exclude<ServiceProps["port"], undefined>;
|
|
377
377
|
logRetention: Exclude<ServiceProps["logRetention"], undefined>;
|
|
378
|
-
waitForInvalidation: Exclude<ServiceProps["waitForInvalidation"], undefined>;
|
|
379
378
|
};
|
|
380
379
|
/**
|
|
381
380
|
* The `Service` construct is a higher level CDK construct that makes it easy to create modern web apps with Server Side Rendering capabilities.
|
package/constructs/Service.js
CHANGED
package/constructs/SsrSite.d.ts
CHANGED
|
@@ -214,21 +214,6 @@ export interface SsrSiteProps {
|
|
|
214
214
|
* ```
|
|
215
215
|
*/
|
|
216
216
|
textEncoding?: "utf-8" | "iso-8859-1" | "windows-1252" | "ascii" | "none";
|
|
217
|
-
/**
|
|
218
|
-
* The strategy to use for invalidating the CDN cache. By default, the CDN cache will invalidate on changes any cached file, but this could become slow on very large projects.
|
|
219
|
-
* - "never" - No invalidation will be performed.
|
|
220
|
-
* - "all" - All files will be invalidated when any file changes. (Default, requires checking file content which will increase deployment time)
|
|
221
|
-
* - "versioned" - Only versioned files will be invalidated when versioned files change.
|
|
222
|
-
* - "always" - All files are invalidated on every deployment.
|
|
223
|
-
* @default all
|
|
224
|
-
* @example
|
|
225
|
-
* ```js
|
|
226
|
-
* assets: {
|
|
227
|
-
* cdnInvalidationStrategy: "versioned"
|
|
228
|
-
* }
|
|
229
|
-
* ```
|
|
230
|
-
*/
|
|
231
|
-
cdnInvalidationStrategy?: "never" | "all" | "versioned" | "always";
|
|
232
217
|
/**
|
|
233
218
|
* The TTL for versioned files (ex: `main-1234.css`) in the CDN and browser cache. Ignored when `versionedFilesCacheHeader` is specified.
|
|
234
219
|
* @default 1 year
|
|
@@ -294,9 +279,45 @@ export interface SsrSiteProps {
|
|
|
294
279
|
*/
|
|
295
280
|
_uploadConcurrency?: number;
|
|
296
281
|
};
|
|
282
|
+
invalidation?: {
|
|
283
|
+
/**
|
|
284
|
+
* While deploying, SST waits for the CloudFront cache invalidation process to finish. This ensures that the new content will be served once the deploy command finishes. However, this process can sometimes take more than 5 mins. For non-prod environments it might make sense to pass in `false`. That'll skip waiting for the cache to invalidate and speed up the deploy process.
|
|
285
|
+
* @default false
|
|
286
|
+
* @example
|
|
287
|
+
* ```js
|
|
288
|
+
* invalidation: {
|
|
289
|
+
* wait: true,
|
|
290
|
+
* }
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
wait?: boolean;
|
|
294
|
+
/**
|
|
295
|
+
* The paths to invalidate. There are three built-in options:
|
|
296
|
+
* - "none" - No invalidation will be performed.
|
|
297
|
+
* - "all" - All files will be invalidated when any file changes.
|
|
298
|
+
* - "versioned" - Only versioned files will be invalidated when versioned files change.
|
|
299
|
+
* Alternatively you can pass in an array of paths to invalidate.
|
|
300
|
+
* @default "all"
|
|
301
|
+
* @example
|
|
302
|
+
* Disable invalidation:
|
|
303
|
+
* ```js
|
|
304
|
+
* invalidation: {
|
|
305
|
+
* paths: "none",
|
|
306
|
+
* }
|
|
307
|
+
* ```
|
|
308
|
+
* Invalidate "index.html" and all files under the "products" route:
|
|
309
|
+
* ```js
|
|
310
|
+
* invalidation: {
|
|
311
|
+
* paths: ["/index.html", "/products/*"],
|
|
312
|
+
* }
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
paths?: "none" | "all" | "versioned" | string[];
|
|
316
|
+
};
|
|
297
317
|
/**
|
|
298
318
|
* While deploying, SST waits for the CloudFront cache invalidation process to finish. This ensures that the new content will be served once the deploy command finishes. However, this process can sometimes take more than 5 mins. For non-prod environments it might make sense to pass in `false`. That'll skip waiting for the cache to invalidate and speed up the deploy process.
|
|
299
319
|
* @default false
|
|
320
|
+
* @deprecated Use `invalidation.wait` instead.
|
|
300
321
|
*/
|
|
301
322
|
waitForInvalidation?: boolean;
|
|
302
323
|
cdk?: {
|
|
@@ -343,13 +364,16 @@ export interface SsrSiteProps {
|
|
|
343
364
|
server?: Pick<CdkFunctionProps, "layers" | "vpc" | "vpcSubnets" | "securityGroups" | "allowAllOutbound" | "allowPublicSubnet" | "architecture" | "logRetention"> & Pick<FunctionProps, "copyFiles">;
|
|
344
365
|
};
|
|
345
366
|
}
|
|
367
|
+
type SsrSiteInvalidationNormalizedProps = Exclude<SsrSiteProps["invalidation"], undefined>;
|
|
346
368
|
export type SsrSiteNormalizedProps = SsrSiteProps & {
|
|
347
369
|
path: Exclude<SsrSiteProps["path"], undefined>;
|
|
348
370
|
typesPath: Exclude<SsrSiteProps["typesPath"], undefined>;
|
|
349
371
|
runtime: Exclude<SsrSiteProps["runtime"], undefined>;
|
|
350
372
|
timeout: Exclude<SsrSiteProps["timeout"], undefined>;
|
|
351
373
|
memorySize: Exclude<SsrSiteProps["memorySize"], undefined>;
|
|
352
|
-
|
|
374
|
+
invalidation: Exclude<SsrSiteProps["invalidation"], undefined> & {
|
|
375
|
+
paths: Exclude<SsrSiteInvalidationNormalizedProps["paths"], undefined>;
|
|
376
|
+
};
|
|
353
377
|
};
|
|
354
378
|
/**
|
|
355
379
|
* The `SsrSite` construct is a higher level CDK construct that makes it easy to create modern web apps with Server Side Rendering capabilities.
|