Skip to content

Commit

Permalink
refactor passing of next config from env to file
Browse files Browse the repository at this point in the history
  • Loading branch information
khuezy committed Dec 5, 2022
1 parent 52105ac commit 22b1aa6
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,31 @@ import { defaultConfig, NextConfigComplete } from 'next/dist/server/config-share
import { imageOptimizer as nextImageOptimizer, ImageOptimizerCache } from 'next/dist/server/image-optimizer'
import { NextUrlWithParsedQuery } from 'next/dist/server/request-meta'
import { ImageConfigComplete, ImageConfig } from 'next/dist/shared/lib/image-config'
import { NextConfig } from 'next'
import { Writable } from 'node:stream'
import https from 'node:https'
import path from 'node:path'
import fs from 'node:fs'

const sourceBucket = process.env.S3_SOURCE_BUCKET ?? undefined

/**
* The imageConfig object is stringified and passthrough the lambda as an environment variable
*/
const _loadImageConfig = (): ImageConfig => {
let imageConfig: ImageConfig = {}
const imageConfigString = process.env.NEXT_IMAGE_CONFIG
try {
imageConfig = JSON.parse(imageConfigString || '{}') as ImageConfig
} catch (err) {
console.error('Error parsing image config: ', { err, imageConfigString })
throw err
}
return imageConfig
}
// The next config file was bundled and outputted to the root
// SEE: src/ImageOptimizationLambda.ts, Line 81
const requiredServerFilesPath = path.join(__dirname, 'required-server-files.json');
const json = fs.readFileSync(requiredServerFilesPath, 'utf-8');
const { config } = JSON.parse(json) as { version: number; config: NextConfig };

const pipeRes = (p: Writable, res: ServerResponse) => {
p.pipe(res)
.once('close', () => {
res.statusCode = 200
res.end()
})
.once('error', (err) => {
console.error('Failed to get image', { err })
res.statusCode = 400
res.end()
})
const pipeRes = (w: Writable, res: ServerResponse) => {
w.pipe(res)
.once('close', () => {
res.statusCode = 200
res.end()
})
.once('error', (err) => {
console.error('Failed to get image', { err })
res.statusCode = 400
res.end()
})
}

// Handle fetching of S3 object before optimization happens in nextjs.
Expand Down Expand Up @@ -73,6 +67,7 @@ const requestHandler =
const stream = response.Body as Writable
pipeRes(stream, res)

// Respect the bucket file's content-type and cace-control
if (response.ContentType) {
res.setHeader('Content-Type', response.ContentType)
}
Expand All @@ -86,15 +81,11 @@ const requestHandler =
const normalizeHeaders = (headers: Record<string, any>) =>
Object.entries(headers).reduce((acc, [key, value]) => ({ ...acc, [key.toLowerCase()]: value }), {} as Record<string, string>)

// Load the config from user's app next.config.js passed via env
const imageConfig = _loadImageConfig()

const nextConfig: NextConfigComplete = {
...(defaultConfig as NextConfigComplete),
images: {
...(defaultConfig.images as ImageConfigComplete),
domains: imageConfig.domains || [],
remotePatterns: imageConfig.remotePatterns || [],
...config.images
},
}

Expand Down
31 changes: 18 additions & 13 deletions src/ImageOptimizationLambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Construct } from 'constructs';
import { NextjsBaseProps } from './NextjsBase';
import type { NextjsBuild } from './NextjsBuild';
import { NextjsLayer } from './NextjsLayer';
// import { config } from 'process';

export type RemotePattern = {
protocol: string;
Expand Down Expand Up @@ -49,8 +50,8 @@ export class ImageOptimizationLambda extends NodejsFunction {

constructor(scope: Construct, id: string, props: ImageOptimizationProps) {
const { lambdaOptions, bucket } = props;
const lambdaPath = path.resolve(__dirname, '../assets/lambda');
const imageOptHandlerPath = path.resolve(lambdaPath, 'ImageOptimization.ts');
const lambdaPath = path.resolve(__dirname, '../assets/lambda/ImageOptimization');
const imageOptHandlerPath = path.resolve(lambdaPath, 'index.ts');

/**
* NOTE: This needs to be configured before calling super(), otherwise the build
Expand All @@ -61,15 +62,27 @@ export class ImageOptimizationLambda extends NodejsFunction {
* in `imageOptimization.ts` to minimize the function size.
*/
const source = path.join(props.nextBuild.nextDir, 'node_modules/next');
const modules = path.join(lambdaPath, 'node_modules');
const target = path.join(modules, 'next');
if (!fs.existsSync(modules)) fs.mkdirSync(modules);
const modulesPath = path.join(lambdaPath, 'node_modules');
const target = path.join(modulesPath, 'next');
if (!fs.existsSync(modulesPath)) fs.mkdirSync(modulesPath);
if (!fs.existsSync(target)) fs.symlinkSync(source, target, 'dir');

// Read the nextjs server config and write contents to bundle output via hooks
const configFile = 'required-server-files.json';
const requiredServerFilesPath = path.join(props.nextBuild.nextStandaloneBuildDir, configFile);
const data = fs.readFileSync(requiredServerFilesPath, 'utf-8');

super(scope, id, {
entry: imageOptHandlerPath,
runtime: RUNTIME,
bundling: {
commandHooks: {
beforeBundling(inputDir: string, outputDir: string): string[] {
return [`echo '${data}' > ${inputDir}/${configFile}`, `cp ${inputDir}/${configFile} ${outputDir}`];
},
afterBundling() { return [] },
beforeInstall() { return [] },
},
minify: true,
target: 'node18',
externalModules: ['@aws-sdk/client-s3'],
Expand All @@ -80,22 +93,14 @@ export class ImageOptimizationLambda extends NodejsFunction {
memorySize: lambdaOptions?.memorySize || 1024,
timeout: lambdaOptions?.timeout ?? Duration.seconds(10),
environment: {
...lambdaOptions?.environment,
S3_SOURCE_BUCKET: bucket.bucketName,
},
});

this.bucket = bucket;
this.loadNextNextConfig(props.nextBuild.imagesManifestPath).catch((err) => {
console.error('Error: ', { err });
});
this.addPolicy();
}

private async loadNextNextConfig(p: string) {
const nextConfig = await import(p);
this.addEnvironment('NEXT_IMAGE_CONFIG', JSON.stringify(nextConfig.images));
}
/**
* Adds policy statement to give GetObject permission Image Optimization lambda.
*/
Expand Down
9 changes: 1 addition & 8 deletions src/NextjsBuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export class NextjsBuild extends Construct {
public tempBuildDir: string;

public nextDir: string;
public imagesManifestPath: string;

constructor(scope: Construct, id: string, props: NextjsBuildProps) {
super(scope, id);
Expand Down Expand Up @@ -83,12 +82,8 @@ export class NextjsBuild extends Construct {
this.nextStandaloneBuildDir = this._getNextStandaloneBuildDir();
this.nextPublicDir = this._getNextPublicDir();
this.nextStaticDir = this._getNextStaticDir();

this.buildPath = this.nextStandaloneBuildDir;

this.nextDir = this._getNextDir();
// image manifest
this.imagesManifestPath = this._getImagesManifestPath();
}

private runNpmBuild() {
Expand Down Expand Up @@ -201,9 +196,7 @@ export class NextjsBuild extends Construct {
private _getNextPublicDir() {
return path.join(this._getNextDir(), NEXTJS_PUBLIC_DIR);
}
private _getImagesManifestPath() {
return path.join(this._getNextDir(), IMAGE_MANIFEST_FILE);
}

}

export interface CreateArchiveArgs {
Expand Down

0 comments on commit 22b1aa6

Please sign in to comment.