From b411feed7d62b017d599db3756fa6586adcd2b43 Mon Sep 17 00:00:00 2001 From: Marcelo Luiz Onhate Date: Thu, 4 Jan 2024 09:03:16 -0300 Subject: [PATCH 1/3] fix: destroy with no bundling --- src/NextjsBuild.ts | 59 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/src/NextjsBuild.ts b/src/NextjsBuild.ts index 85c5491e..db60b121 100644 --- a/src/NextjsBuild.ts +++ b/src/NextjsBuild.ts @@ -5,12 +5,12 @@ import { Stack, Token } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { NEXTJS_BUILD_DIR, + NEXTJS_BUILD_DYNAMODB_PROVIDER_FN_DIR, NEXTJS_BUILD_IMAGE_FN_DIR, NEXTJS_BUILD_REVALIDATE_FN_DIR, NEXTJS_BUILD_SERVER_FN_DIR, - NEXTJS_STATIC_DIR, NEXTJS_CACHE_DIR, - NEXTJS_BUILD_DYNAMODB_PROVIDER_FN_DIR, + NEXTJS_STATIC_DIR, } from './constants'; import type { NextjsProps } from './Nextjs'; import { NextjsBucketDeployment } from './NextjsBucketDeployment'; @@ -103,9 +103,28 @@ export class NextjsBuild extends Construct { super(scope, id); this.props = props; this.validatePaths(); - // when `cdk deploy "NonNextjsStack" --exclusively` is run, don't run build - if (Stack.of(this).bundlingRequired && !this.props.skipBuild) { - this.build(); + + const bundlingRequired = Stack.of(this).bundlingRequired; + const skipBuild = this.props.skipBuild; + + /** + * | bundlingRequired | skipBuild | Scenario | Action | + * |------------------|-----------|---------------------------------|---------------------------------------------------| + * | true | true | deploy/synth with reused bundle | no build, check .open-next exists, fail if not | + * | true | false | regular deploy/synth | build, .open-next will exist | + * | false | false | destroy | no build, check if .open-next exists, if not mock | + * | false | true | destroy with reused bundle? | no build, check if .open-next exists, if not mock | + */ + if (bundlingRequired) { + // deploy/synth + if (skipBuild) { + this.assertBuildDirExists(true); + } else { + this.build(); + } + } else { + // destroy + this.mockNextBuildDir(); } } @@ -171,6 +190,17 @@ export class NextjsBuild extends Construct { return listDirectory(this.nextStaticDir).map((file) => path.join('/', path.relative(this.nextStaticDir, file))); } + private assertBuildDirExists(throwIfMissing = true) { + const dir = this.getNextBuildDir(); + if (!fs.existsSync(dir)) { + if (throwIfMissing) { + throw new Error(`Build directory "${dir}" does not exist. Try removing skipBuild: true option.`); + } + return false; + } + return true; + } + private getNextBuildDir(): string { const dir = path.resolve(this.props.nextjsPath, NEXTJS_BUILD_DIR); this.warnIfMissing(dir); @@ -182,4 +212,23 @@ export class NextjsBuild extends Construct { console.warn(`Warning: ${dir} does not exist.`); } } + + private mockNextBuildDir() { + function createMockDirAndFile(dir: string) { + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync(path.join(dir, 'package.json'), '{}', 'utf8'); + } + + const buildDirExists = this.assertBuildDirExists(false); + if (!buildDirExists) { + // mock .open-next + createMockDirAndFile(this.getNextBuildDir()); + createMockDirAndFile(this.nextServerFnDir); + createMockDirAndFile(this.nextImageFnDir); + createMockDirAndFile(this.nextRevalidateFnDir); + createMockDirAndFile(this.nextRevalidateDynamoDBProviderFnDir); + createMockDirAndFile(this.nextStaticDir); + createMockDirAndFile(this.nextCacheDir); + } + } } From 23c9f876bbb1bcbe2e6ca06c2c3402f20bb802bf Mon Sep 17 00:00:00 2001 From: Marcelo Luiz Onhate Date: Thu, 4 Jan 2024 11:49:22 -0300 Subject: [PATCH 2/3] updated docs --- docs/code-deployment-flow.md | 18 ++++++++++++++++++ src/NextjsBuild.ts | 13 +++---------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/docs/code-deployment-flow.md b/docs/code-deployment-flow.md index 51dececc..1cbfafe6 100644 --- a/docs/code-deployment-flow.md +++ b/docs/code-deployment-flow.md @@ -14,6 +14,24 @@ Deep dive into `Nextjs` constructs code deployment flow - how your Next.js code _Only applicable for PNPM Monorepos_ PNPM Monorepos use symlinks between workspace node_modules and the top level node_modules. CDK Assets do not support symlinks despite the configuration options available. Therefore, we must zip up the assets ourselves. Also, `nextjs-bucket-deployment.ts` handles symlinks to unzip and zip symlinks within Lambda Custom Resources (for ServerFnBucketDeployment). + +## Conditional Build Logic + +`NextjjsBuild` will use the following logic to determine if a build is required or not to proceed. + +``` +| bundlingRequired | skipBuild | Scenario | Action | +|------------------|-----------|---------------------------------|---------------------------------------------------| +| true | true | deploy/synth with reused bundle | no build, check .open-next exists, fail if not | +| true | false | regular deploy/synth | build, .open-next will exist | +| false | false | destroy | no build, check if .open-next exists, if not mock | +| false | true | destroy with reused bundle | no build, check if .open-next exists, if not mock | +``` + +*bundlingRequired* = `Stack.of(this).bundlingRequired` [see](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stack.html#bundlingrequired) +*skipBuild* = `NextjsProps.skipBuild` + + Relevant GitHub Issues: - https://github.com/aws/aws-cdk/issues/9251 - https://github.com/Stuk/jszip/issues/386#issuecomment-634773343 diff --git a/src/NextjsBuild.ts b/src/NextjsBuild.ts index db60b121..115d3da9 100644 --- a/src/NextjsBuild.ts +++ b/src/NextjsBuild.ts @@ -1,8 +1,8 @@ +import { Stack, Token } from 'aws-cdk-lib'; import { execSync } from 'child_process'; +import { Construct } from 'constructs'; import * as fs from 'fs'; import * as path from 'path'; -import { Stack, Token } from 'aws-cdk-lib'; -import { Construct } from 'constructs'; import { NEXTJS_BUILD_DIR, NEXTJS_BUILD_DYNAMODB_PROVIDER_FN_DIR, @@ -107,14 +107,7 @@ export class NextjsBuild extends Construct { const bundlingRequired = Stack.of(this).bundlingRequired; const skipBuild = this.props.skipBuild; - /** - * | bundlingRequired | skipBuild | Scenario | Action | - * |------------------|-----------|---------------------------------|---------------------------------------------------| - * | true | true | deploy/synth with reused bundle | no build, check .open-next exists, fail if not | - * | true | false | regular deploy/synth | build, .open-next will exist | - * | false | false | destroy | no build, check if .open-next exists, if not mock | - * | false | true | destroy with reused bundle? | no build, check if .open-next exists, if not mock | - */ + // for more info see docs/code-deployment-flow.md Conditional Build Logic section if (bundlingRequired) { // deploy/synth if (skipBuild) { From 7757a7260afda56df8a3b9939637b93928d3f928 Mon Sep 17 00:00:00 2001 From: Marcelo Luiz Onhate Date: Thu, 4 Jan 2024 12:51:53 -0300 Subject: [PATCH 3/3] yarn build --- src/NextjsBuild.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NextjsBuild.ts b/src/NextjsBuild.ts index 115d3da9..10e7cbf3 100644 --- a/src/NextjsBuild.ts +++ b/src/NextjsBuild.ts @@ -1,8 +1,8 @@ -import { Stack, Token } from 'aws-cdk-lib'; import { execSync } from 'child_process'; -import { Construct } from 'constructs'; import * as fs from 'fs'; import * as path from 'path'; +import { Stack, Token } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; import { NEXTJS_BUILD_DIR, NEXTJS_BUILD_DYNAMODB_PROVIDER_FN_DIR,