Skip to content

Commit

Permalink
New option to install packages (#574)
Browse files Browse the repository at this point in the history
* added new option to install packages

* fix lint

* review

Co-authored-by: Victor Berchet <victor@suumit.com>

* changeset & lint fix

---------

Co-authored-by: Victor Berchet <victor@suumit.com>
  • Loading branch information
conico974 and vicb authored Oct 24, 2024
1 parent ec64cf0 commit 216e05c
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 34 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilled-frogs-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@opennextjs/aws": minor
---

Add a new option to install native dependencies on every lambda
6 changes: 6 additions & 0 deletions packages/open-next/src/build/compileTagCacheProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import path from "node:path";

import { openNextResolvePlugin } from "../plugins/resolve.js";
import * as buildHelper from "./helper.js";
import { installDependencies } from "./installDeps.js";

export async function compileTagCacheProvider(
options: buildHelper.BuildOptions,
Expand Down Expand Up @@ -30,4 +31,9 @@ export async function compileTagCacheProvider(
},
options,
);

installDependencies(
providerPath,
options.config.initializationFunction?.install,
);
}
33 changes: 8 additions & 25 deletions packages/open-next/src/build/createImageOptimizationBundle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import cp from "node:child_process";
import fs from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
Expand All @@ -7,6 +6,7 @@ import logger from "../logger.js";
import { openNextReplacementPlugin } from "../plugins/replacement.js";
import { openNextResolvePlugin } from "../plugins/resolve.js";
import * as buildHelper from "./helper.js";
import { installDependencies } from "./installDeps.js";

const require = createRequire(import.meta.url);

Expand All @@ -15,7 +15,7 @@ export async function createImageOptimizationBundle(
) {
logger.info(`Bundling image optimization function...`);

const { appPath, appBuildOutputPath, config, outputDir } = options;
const { appBuildOutputPath, config, outputDir } = options;

// Create output folder
const outputPath = path.join(outputDir, "image-optimization-function");
Expand Down Expand Up @@ -108,29 +108,12 @@ export async function createImageOptimizationBundle(
// Target should be same as used by Lambda, see https://github.com/sst/sst/blob/ca6f763fdfddd099ce2260202d0ce48c72e211ea/packages/sst/src/constructs/NextjsSite.ts#L114
// For SHARP_IGNORE_GLOBAL_LIBVIPS see: https://github.com/lovell/sharp/blob/main/docs/install.md#aws-lambda

const nodeOutputPath = path.resolve(outputPath);
const sharpVersion = process.env.SHARP_VERSION ?? "0.32.6";

const arch = config.imageOptimization?.arch ?? "arm64";
const nodeVersion = config.imageOptimization?.nodeVersion ?? "18";

//check if we are running in Windows environment then set env variables accordingly.
try {
cp.execSync(
// We might want to change the arch args to cpu args, it seems to be the documented way
`npm install --arch=${arch} --platform=linux --target=${nodeVersion} --libc=glibc --prefix="${nodeOutputPath}" sharp@${sharpVersion}`,
{
stdio: "pipe",
cwd: appPath,
env: {
...process.env,
SHARP_IGNORE_GLOBAL_LIBVIPS: "1",
},
},
);
} catch (e: any) {
logger.error(e.stdout.toString());
logger.error(e.stderr.toString());
logger.error("Failed to install sharp.");
}
installDependencies(
outputPath,
config.imageOptimization?.install ?? {
packages: [`sharp@${sharpVersion}`],
},
);
}
3 changes: 3 additions & 0 deletions packages/open-next/src/build/createMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import logger from "../logger.js";
import { type MiddlewareManifest } from "../types/next-types.js";
import { buildEdgeBundle } from "./edge/createEdgeBundle.js";
import * as buildHelper from "./helper.js";
import { installDependencies } from "./installDeps.js";

/**
* Compiles the middleware bundle.
Expand Down Expand Up @@ -64,6 +65,8 @@ export async function createMiddleware(options: buildHelper.BuildOptions) {
includeCache: config.dangerous?.enableCacheInterception,
additionalExternals: config.edgeExternals,
});

installDependencies(outputPath, config.middleware?.install);
} else {
await buildEdgeBundle({
entrypoint: path.join(
Expand Down
3 changes: 3 additions & 0 deletions packages/open-next/src/build/createRevalidationBundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import path from "node:path";
import logger from "../logger.js";
import { openNextResolvePlugin } from "../plugins/resolve.js";
import * as buildHelper from "./helper.js";
import { installDependencies } from "./installDeps.js";

export async function createRevalidationBundle(
options: buildHelper.BuildOptions,
Expand Down Expand Up @@ -41,6 +42,8 @@ export async function createRevalidationBundle(
options,
);

installDependencies(outputPath, config.revalidate?.install);

// Copy over .next/prerender-manifest.json file
fs.copyFileSync(
path.join(appBuildOutputPath, ".next", "prerender-manifest.json"),
Expand Down
3 changes: 3 additions & 0 deletions packages/open-next/src/build/createServerBundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { compileCache } from "./compileCache.js";
import { copyTracedFiles } from "./copyTracedFiles.js";
import { generateEdgeBundle } from "./edge/createEdgeBundle.js";
import * as buildHelper from "./helper.js";
import { installDependencies } from "./installDeps.js";

const require = createRequire(import.meta.url);

Expand Down Expand Up @@ -267,6 +268,8 @@ async function generateBundle(
addMonorepoEntrypoint(outputPath, packagePath);
}

installDependencies(outputPath, fnOptions.install);

if (fnOptions.minify) {
await minifyServerBundle(outputPath);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/open-next/src/build/createWarmerBundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import path from "node:path";
import logger from "../logger.js";
import { openNextResolvePlugin } from "../plugins/resolve.js";
import * as buildHelper from "./helper.js";
import { installDependencies } from "./installDeps.js";

export async function createWarmerBundle(options: buildHelper.BuildOptions) {
logger.info(`Bundling warmer function...`);
Expand Down Expand Up @@ -48,4 +49,6 @@ export async function createWarmerBundle(options: buildHelper.BuildOptions) {
},
options,
);

installDependencies(outputPath, config.warmer?.install);
}
55 changes: 55 additions & 0 deletions packages/open-next/src/build/installDeps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";

import { execSync } from "child_process";
import { InstallOptions } from "types/open-next";

import logger from "../logger.js";

export function installDependencies(
outputDir: string,
installOptions?: InstallOptions,
) {
try {
if (!installOptions) {
return;
}
const name = outputDir.split("/").pop();
// First we create a tempDir
const tempInstallDir = fs.mkdtempSync(
path.join(os.tmpdir(), `open-next-install-${name}`),
);
logger.info(`Installing dependencies for ${name}...`);
// We then need to run install in the tempDir
// We don't install in the output dir directly because it could contain a package.json, and npm would then try to reinstall not complete deps from tracing the files
const installCommand = `npm install --arch=${
installOptions.arch ?? "arm64"
} --platform=linux --target=${installOptions.nodeVersion ?? "18"} --libc=${
installOptions.libc ?? "glibc"
} ${installOptions.packages.join(" ")}`;
execSync(installCommand, {
stdio: "pipe",
cwd: tempInstallDir,
env: {
...process.env,
SHARP_IGNORE_GLOBAL_LIBVIPS: "1",
},
});

// Copy the node_modules to the outputDir
fs.cpSync(
path.join(tempInstallDir, "node_modules"),
path.join(outputDir, "node_modules"),

{ recursive: true, force: true, dereference: true },
);

// Cleanup tempDir
fs.rmSync(tempInstallDir, { recursive: true, force: true });
logger.info(`Dependencies installed for ${name}`);
} catch (e: any) {
logger.error(e.stdout.toString());
logger.error("Could not install dependencies");
}
}
42 changes: 33 additions & 9 deletions packages/open-next/src/types/open-next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,31 @@ export interface OverrideOptions extends DefaultOverrideOptions {
queue?: IncludedQueue | LazyLoadedOverride<Queue>;
}

export interface InstallOptions {
/**
* List of packages to install
* @example
* ```ts
* install: {
* packages: ["sharp@0.32"]
* }
* ```
*/
packages: string[];
/**
* @default "arm64"
*/
arch?: "x64" | "arm64";
/**
* @default "18"
*/
nodeVersion?: "18" | "20" | "22";
/**
* @default "glibc"
*/
libc?: "glibc" | "musl";
}

export interface DefaultFunctionOptions<
E extends BaseEventOrResult = InternalEvent,
R extends BaseEventOrResult = InternalResult,
Expand All @@ -206,6 +231,14 @@ export interface DefaultFunctionOptions<
* Enable overriding the default lambda.
*/
override?: DefaultOverrideOptions<E, R>;

/**
* Install options for the function.
* This is used to install additional packages to this function.
* For image optimization, it will install sharp by default.
* @default undefined
*/
install?: InstallOptions;
}

export interface FunctionOptions extends DefaultFunctionOptions {
Expand Down Expand Up @@ -324,15 +357,6 @@ export interface OpenNextConfig {
* @default "s3"
*/
loader?: IncludedImageLoader | LazyLoadedOverride<ImageLoader>;
/**
* @default "arm64"
*/
arch?: "x64" | "arm64";
/**
* @default "18"
*/

nodeVersion?: "18" | "20";
};

/**
Expand Down

0 comments on commit 216e05c

Please sign in to comment.