Skip to content

Commit 216e05c

Browse files
conico974vicb
andauthored
New option to install packages (#574)
* added new option to install packages * fix lint * review Co-authored-by: Victor Berchet <[email protected]> * changeset & lint fix --------- Co-authored-by: Victor Berchet <[email protected]>
1 parent ec64cf0 commit 216e05c

File tree

9 files changed

+119
-34
lines changed

9 files changed

+119
-34
lines changed

.changeset/chilled-frogs-draw.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/aws": minor
3+
---
4+
5+
Add a new option to install native dependencies on every lambda

packages/open-next/src/build/compileTagCacheProvider.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from "node:path";
22

33
import { openNextResolvePlugin } from "../plugins/resolve.js";
44
import * as buildHelper from "./helper.js";
5+
import { installDependencies } from "./installDeps.js";
56

67
export async function compileTagCacheProvider(
78
options: buildHelper.BuildOptions,
@@ -30,4 +31,9 @@ export async function compileTagCacheProvider(
3031
},
3132
options,
3233
);
34+
35+
installDependencies(
36+
providerPath,
37+
options.config.initializationFunction?.install,
38+
);
3339
}

packages/open-next/src/build/createImageOptimizationBundle.ts

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import cp from "node:child_process";
21
import fs from "node:fs";
32
import { createRequire } from "node:module";
43
import path from "node:path";
@@ -7,6 +6,7 @@ import logger from "../logger.js";
76
import { openNextReplacementPlugin } from "../plugins/replacement.js";
87
import { openNextResolvePlugin } from "../plugins/resolve.js";
98
import * as buildHelper from "./helper.js";
9+
import { installDependencies } from "./installDeps.js";
1010

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

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

18-
const { appPath, appBuildOutputPath, config, outputDir } = options;
18+
const { appBuildOutputPath, config, outputDir } = options;
1919

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

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

114-
const arch = config.imageOptimization?.arch ?? "arm64";
115-
const nodeVersion = config.imageOptimization?.nodeVersion ?? "18";
116-
117-
//check if we are running in Windows environment then set env variables accordingly.
118-
try {
119-
cp.execSync(
120-
// We might want to change the arch args to cpu args, it seems to be the documented way
121-
`npm install --arch=${arch} --platform=linux --target=${nodeVersion} --libc=glibc --prefix="${nodeOutputPath}" sharp@${sharpVersion}`,
122-
{
123-
stdio: "pipe",
124-
cwd: appPath,
125-
env: {
126-
...process.env,
127-
SHARP_IGNORE_GLOBAL_LIBVIPS: "1",
128-
},
129-
},
130-
);
131-
} catch (e: any) {
132-
logger.error(e.stdout.toString());
133-
logger.error(e.stderr.toString());
134-
logger.error("Failed to install sharp.");
135-
}
113+
installDependencies(
114+
outputPath,
115+
config.imageOptimization?.install ?? {
116+
packages: [`sharp@${sharpVersion}`],
117+
},
118+
);
136119
}

packages/open-next/src/build/createMiddleware.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import logger from "../logger.js";
55
import { type MiddlewareManifest } from "../types/next-types.js";
66
import { buildEdgeBundle } from "./edge/createEdgeBundle.js";
77
import * as buildHelper from "./helper.js";
8+
import { installDependencies } from "./installDeps.js";
89

910
/**
1011
* Compiles the middleware bundle.
@@ -64,6 +65,8 @@ export async function createMiddleware(options: buildHelper.BuildOptions) {
6465
includeCache: config.dangerous?.enableCacheInterception,
6566
additionalExternals: config.edgeExternals,
6667
});
68+
69+
installDependencies(outputPath, config.middleware?.install);
6770
} else {
6871
await buildEdgeBundle({
6972
entrypoint: path.join(

packages/open-next/src/build/createRevalidationBundle.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from "node:path";
44
import logger from "../logger.js";
55
import { openNextResolvePlugin } from "../plugins/resolve.js";
66
import * as buildHelper from "./helper.js";
7+
import { installDependencies } from "./installDeps.js";
78

89
export async function createRevalidationBundle(
910
options: buildHelper.BuildOptions,
@@ -41,6 +42,8 @@ export async function createRevalidationBundle(
4142
options,
4243
);
4344

45+
installDependencies(outputPath, config.revalidate?.install);
46+
4447
// Copy over .next/prerender-manifest.json file
4548
fs.copyFileSync(
4649
path.join(appBuildOutputPath, ".next", "prerender-manifest.json"),

packages/open-next/src/build/createServerBundle.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { compileCache } from "./compileCache.js";
1717
import { copyTracedFiles } from "./copyTracedFiles.js";
1818
import { generateEdgeBundle } from "./edge/createEdgeBundle.js";
1919
import * as buildHelper from "./helper.js";
20+
import { installDependencies } from "./installDeps.js";
2021

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

@@ -267,6 +268,8 @@ async function generateBundle(
267268
addMonorepoEntrypoint(outputPath, packagePath);
268269
}
269270

271+
installDependencies(outputPath, fnOptions.install);
272+
270273
if (fnOptions.minify) {
271274
await minifyServerBundle(outputPath);
272275
}

packages/open-next/src/build/createWarmerBundle.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from "node:path";
44
import logger from "../logger.js";
55
import { openNextResolvePlugin } from "../plugins/resolve.js";
66
import * as buildHelper from "./helper.js";
7+
import { installDependencies } from "./installDeps.js";
78

89
export async function createWarmerBundle(options: buildHelper.BuildOptions) {
910
logger.info(`Bundling warmer function...`);
@@ -48,4 +49,6 @@ export async function createWarmerBundle(options: buildHelper.BuildOptions) {
4849
},
4950
options,
5051
);
52+
53+
installDependencies(outputPath, config.warmer?.install);
5154
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import fs from "node:fs";
2+
import os from "node:os";
3+
import path from "node:path";
4+
5+
import { execSync } from "child_process";
6+
import { InstallOptions } from "types/open-next";
7+
8+
import logger from "../logger.js";
9+
10+
export function installDependencies(
11+
outputDir: string,
12+
installOptions?: InstallOptions,
13+
) {
14+
try {
15+
if (!installOptions) {
16+
return;
17+
}
18+
const name = outputDir.split("/").pop();
19+
// First we create a tempDir
20+
const tempInstallDir = fs.mkdtempSync(
21+
path.join(os.tmpdir(), `open-next-install-${name}`),
22+
);
23+
logger.info(`Installing dependencies for ${name}...`);
24+
// We then need to run install in the tempDir
25+
// 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
26+
const installCommand = `npm install --arch=${
27+
installOptions.arch ?? "arm64"
28+
} --platform=linux --target=${installOptions.nodeVersion ?? "18"} --libc=${
29+
installOptions.libc ?? "glibc"
30+
} ${installOptions.packages.join(" ")}`;
31+
execSync(installCommand, {
32+
stdio: "pipe",
33+
cwd: tempInstallDir,
34+
env: {
35+
...process.env,
36+
SHARP_IGNORE_GLOBAL_LIBVIPS: "1",
37+
},
38+
});
39+
40+
// Copy the node_modules to the outputDir
41+
fs.cpSync(
42+
path.join(tempInstallDir, "node_modules"),
43+
path.join(outputDir, "node_modules"),
44+
45+
{ recursive: true, force: true, dereference: true },
46+
);
47+
48+
// Cleanup tempDir
49+
fs.rmSync(tempInstallDir, { recursive: true, force: true });
50+
logger.info(`Dependencies installed for ${name}`);
51+
} catch (e: any) {
52+
logger.error(e.stdout.toString());
53+
logger.error("Could not install dependencies");
54+
}
55+
}

packages/open-next/src/types/open-next.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,31 @@ export interface OverrideOptions extends DefaultOverrideOptions {
188188
queue?: IncludedQueue | LazyLoadedOverride<Queue>;
189189
}
190190

191+
export interface InstallOptions {
192+
/**
193+
* List of packages to install
194+
* @example
195+
* ```ts
196+
* install: {
197+
* packages: ["[email protected]"]
198+
* }
199+
* ```
200+
*/
201+
packages: string[];
202+
/**
203+
* @default "arm64"
204+
*/
205+
arch?: "x64" | "arm64";
206+
/**
207+
* @default "18"
208+
*/
209+
nodeVersion?: "18" | "20" | "22";
210+
/**
211+
* @default "glibc"
212+
*/
213+
libc?: "glibc" | "musl";
214+
}
215+
191216
export interface DefaultFunctionOptions<
192217
E extends BaseEventOrResult = InternalEvent,
193218
R extends BaseEventOrResult = InternalResult,
@@ -206,6 +231,14 @@ export interface DefaultFunctionOptions<
206231
* Enable overriding the default lambda.
207232
*/
208233
override?: DefaultOverrideOptions<E, R>;
234+
235+
/**
236+
* Install options for the function.
237+
* This is used to install additional packages to this function.
238+
* For image optimization, it will install sharp by default.
239+
* @default undefined
240+
*/
241+
install?: InstallOptions;
209242
}
210243

211244
export interface FunctionOptions extends DefaultFunctionOptions {
@@ -324,15 +357,6 @@ export interface OpenNextConfig {
324357
* @default "s3"
325358
*/
326359
loader?: IncludedImageLoader | LazyLoadedOverride<ImageLoader>;
327-
/**
328-
* @default "arm64"
329-
*/
330-
arch?: "x64" | "arm64";
331-
/**
332-
* @default "18"
333-
*/
334-
335-
nodeVersion?: "18" | "20";
336360
};
337361

338362
/**

0 commit comments

Comments
 (0)