Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
);
}
31 changes: 7 additions & 24 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 { 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 @@
) {
logger.info(`Bundling image optimization function...`);

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

Check warning on line 18 in packages/open-next/src/build/createImageOptimizationBundle.ts

View workflow job for this annotation

GitHub Actions / validate

'appPath' is assigned a value but never used. Allowed unused vars must match /^_/u

// Create output folder
const outputPath = path.join(outputDir, "image-optimization-function");
Expand Down Expand Up @@ -108,29 +108,12 @@
// 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";

export async function createMiddleware(options: buildHelper.BuildOptions) {
logger.info(`Bundling middleware function...`);
Expand Down Expand Up @@ -58,6 +59,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);
}
50 changes: 50 additions & 0 deletions packages/open-next/src/build/installDeps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { execSync } from "child_process";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
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(" ")}`;

Check failure on line 25 in packages/open-next/src/build/installDeps.ts

View workflow job for this annotation

GitHub Actions / validate

Replace `installOptions.arch·??·"arm64"}·--platform=linux·--target=${installOptions.nodeVersion·??·"18"}·--libc=${installOptions.libc·??·"glibc"` with `⏎······installOptions.arch·??·"arm64"⏎····}·--platform=linux·--target=${installOptions.nodeVersion·??·"18"}·--libc=${⏎······installOptions.libc·??·"glibc"⏎····`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to use whatever packager was used for Next as it has been retrieved anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, but we'll need to figure out how to pass all these options.
I don't think they support all of them, or there is no docs. If some other package manager support it we could use them and fallback to npm when not possible

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could pass the buildOptions and the function name to installDeps.
It would mean adding the outputPath and the packager there.
But that's probably a nice to have for later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about using buildOptions and name, but i think it would just overcomplicate things.
It could be a splitted functions (which are under functions in the config) and server functions are inside a different folder than the rest.
We could still pass buildOptions to get the packager

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 @@ -184,6 +184,31 @@ export interface OverrideOptions extends DefaultOverrideOptions {
queue?: IncludedQueue | LazyLoadedOverride<Queue>;
}

export interface InstallOptions {
/**
* List of packages to install
* @example
* ```ts
* install: {
* packages: ["[email protected]"]
* }
* ```
*/
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 @@ -202,6 +227,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 @@ -318,15 +351,6 @@ export interface OpenNextConfig {
* @default "s3"
*/
loader?: IncludedImageLoader | LazyLoadedOverride<ImageLoader>;
/**
* @default "arm64"
*/
arch?: "x64" | "arm64";
/**
* @default "18"
*/

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

/**
Expand Down
Loading