@opennextjs/aws 4.1.11 → 4.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -66,7 +66,10 @@ export type { SemverOp } from "../utils/semver.js";
66
66
  export { compareSemver } from "../utils/semver.js";
67
67
  /**
68
68
  * Check if a Next.js version is supported by the Next.js team.
69
+ *
69
70
  * Major versions are supported for 2 years following their initial release.
71
+ * A major version is also considered supported as long as it is the Active or Maintenance LTS,
72
+ * that is until the second next major version is released (i.e. 15 is supported until 17 is released).
70
73
  *
71
74
  * @param version - The Next.js version to check (semver format)
72
75
  * @returns true if the version is currently supported, false otherwise
@@ -235,7 +235,10 @@ const NEXT_RELEASE_DATES = {
235
235
  };
236
236
  /**
237
237
  * Check if a Next.js version is supported by the Next.js team.
238
+ *
238
239
  * Major versions are supported for 2 years following their initial release.
240
+ * A major version is also considered supported as long as it is the Active or Maintenance LTS,
241
+ * that is until the second next major version is released (i.e. 15 is supported until 17 is released).
239
242
  *
240
243
  * @param version - The Next.js version to check (semver format)
241
244
  * @returns true if the version is currently supported, false otherwise
@@ -245,10 +248,14 @@ export function isNextVersionSupported(version) {
245
248
  if (!match) {
246
249
  return false;
247
250
  }
248
- const releaseDateStr = NEXT_RELEASE_DATES[Number(match[0])];
251
+ const major = Number(match[0]);
252
+ const releaseDateStr = NEXT_RELEASE_DATES[major];
249
253
  if (!releaseDateStr) {
250
254
  return false;
251
255
  }
256
+ if (!NEXT_RELEASE_DATES[major + 2]) {
257
+ return true;
258
+ }
252
259
  const releaseDate = new Date(releaseDateStr);
253
260
  const supportEndDate = new Date(releaseDate);
254
261
  supportEndDate.setFullYear(supportEndDate.getFullYear() + 2);
@@ -0,0 +1,49 @@
1
+ import { type CodePatcher } from "./codePatcher.js";
2
+ /**
3
+ * A Next.js package installed on disk.
4
+ */
5
+ export interface NextPackage {
6
+ /** Directory of the package, i.e. the directory of its `package.json`. */
7
+ dir: string;
8
+ /** Version of the package. */
9
+ version: string;
10
+ }
11
+ /**
12
+ * Compute the diff resulting of applying the `rule` to `src`.
13
+ *
14
+ * `diff` is an optional peer dependency of `@opennextjs/aws`: it must be installed to call this function.
15
+ *
16
+ * @param filename Filename used in the patch output
17
+ * @param src Content of the source code
18
+ * @param rule ASTgrep rule
19
+ * @returns diff in unified diff format
20
+ * @throws When the `diff` package is not installed
21
+ */
22
+ export declare function computePatchDiff(filename: string, src: string, rule: string): string;
23
+ /**
24
+ * Get the Next.js packages installed for some applications.
25
+ *
26
+ * Packages are deduplicated by version: a package manager can install the same version in multiple
27
+ * directories (i.e. pnpm when the peer dependencies differ).
28
+ *
29
+ * @param appDirs Directories of the applications (absolute or relative to the current working directory),
30
+ * `next` is resolved from their `package.json`
31
+ * @returns the path and version of the Next.js packages
32
+ * @throws When `next` can not be resolved from one of the applications
33
+ */
34
+ export declare function getInstalledNextPackages(appDirs: string[]): NextPackage[];
35
+ /**
36
+ * Get the files of a Next.js package that a code patcher applies to.
37
+ *
38
+ * It selects the files the same way `applyCodePatches` does at build time,
39
+ * i.e. using the `versions`, the `pathFilter` and the `contentFilter` of the patches.
40
+ *
41
+ * @param next A Next.js package, see `getInstalledNextPackages`
42
+ * @param codePatcher The code patcher
43
+ * @returns the files (path relative to the package) with their content and the patches to apply
44
+ */
45
+ export declare function getNextFilesToPatch(next: NextPackage, codePatcher: CodePatcher): {
46
+ name: string;
47
+ code: string;
48
+ patches: CodePatcher["patches"];
49
+ }[];
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Utilities to test code patches.
3
+ *
4
+ * They are used by the OpenNext adapters to test their patches against frozen snippets and against
5
+ * the files of the Next.js versions they are installed with.
6
+ * This module is not used at build time or at runtime.
7
+ */
8
+ import { readFileSync, readdirSync } from "node:fs";
9
+ import { createRequire } from "node:module";
10
+ import path from "node:path";
11
+ import { patchCode } from "./astCodePatcher.js";
12
+ import { isVersionInRange } from "./codePatcher.js";
13
+ const require = createRequire(import.meta.url);
14
+ /**
15
+ * Compute the diff resulting of applying the `rule` to `src`.
16
+ *
17
+ * `diff` is an optional peer dependency of `@opennextjs/aws`: it must be installed to call this function.
18
+ *
19
+ * @param filename Filename used in the patch output
20
+ * @param src Content of the source code
21
+ * @param rule ASTgrep rule
22
+ * @returns diff in unified diff format
23
+ * @throws When the `diff` package is not installed
24
+ */
25
+ export function computePatchDiff(filename, src, rule) {
26
+ // Loaded lazily so that the other utilities can be used without `diff`.
27
+ const { createPatch } = require("diff");
28
+ const dst = patchCode(src, rule);
29
+ return createPatch(filename, src, dst);
30
+ }
31
+ /**
32
+ * Get the Next.js packages installed for some applications.
33
+ *
34
+ * Packages are deduplicated by version: a package manager can install the same version in multiple
35
+ * directories (i.e. pnpm when the peer dependencies differ).
36
+ *
37
+ * @param appDirs Directories of the applications (absolute or relative to the current working directory),
38
+ * `next` is resolved from their `package.json`
39
+ * @returns the path and version of the Next.js packages
40
+ * @throws When `next` can not be resolved from one of the applications
41
+ */
42
+ export function getInstalledNextPackages(appDirs) {
43
+ const packages = new Map();
44
+ for (const appDir of appDirs) {
45
+ const appRequire = createRequire(path.join(path.resolve(appDir), "package.json"));
46
+ const dir = path.dirname(appRequire.resolve("next/package.json"));
47
+ const { version } = JSON.parse(readFileSync(path.join(dir, "package.json"), "utf-8"));
48
+ if (!packages.has(version)) {
49
+ packages.set(version, dir);
50
+ }
51
+ }
52
+ return [...packages].map(([version, dir]) => ({ dir, version }));
53
+ }
54
+ /**
55
+ * Get the files of a Next.js package that a code patcher applies to.
56
+ *
57
+ * It selects the files the same way `applyCodePatches` does at build time,
58
+ * i.e. using the `versions`, the `pathFilter` and the `contentFilter` of the patches.
59
+ *
60
+ * @param next A Next.js package, see `getInstalledNextPackages`
61
+ * @param codePatcher The code patcher
62
+ * @returns the files (path relative to the package) with their content and the patches to apply
63
+ */
64
+ export function getNextFilesToPatch(next, codePatcher) {
65
+ const patches = codePatcher.patches.filter(({ versions }) => isVersionInRange(next.version, versions));
66
+ return readdirSync(next.dir, { recursive: true }).flatMap((name) => {
67
+ const filePath = path.join(next.dir, name);
68
+ const matchingPath = patches.filter(({ pathFilter }) => filePath.match(pathFilter));
69
+ if (matchingPath.length === 0) {
70
+ return [];
71
+ }
72
+ const code = readFileSync(filePath, "utf-8");
73
+ const matchingContent = matchingPath.filter(({ contentFilter }) => !contentFilter || code.match(contentFilter));
74
+ return matchingContent.length === 0
75
+ ? []
76
+ : [{ name, code, patches: matchingContent }];
77
+ });
78
+ }
package/dist/index.js CHANGED
@@ -1,29 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { build } from "./build.js";
3
+ import { parseArgs } from "./utils/args.js";
3
4
  const command = process.argv[2];
4
5
  if (command !== "build")
5
6
  printHelp();
6
- const args = parseArgs();
7
- if (Object.keys(args).includes("--help"))
7
+ const args = parseArgs(process.argv.slice(2));
8
+ if (args.help)
8
9
  printHelp();
9
- await build(args["--config-path"], args["--node-externals"], args["--dangerously-use-unsupported-next-version"] !== undefined);
10
- function parseArgs() {
11
- return process.argv.slice(2).reduce((acc, key, ind, self) => {
12
- if (key.startsWith("--")) {
13
- if (self[ind + 1] && self[ind + 1].startsWith("-")) {
14
- acc[key] = undefined;
15
- }
16
- else if (self[ind + 1]) {
17
- acc[key] = self[ind + 1];
18
- // eslint-disable-next-line sonarjs/elseif-without-else
19
- }
20
- else if (!self[ind + 1]) {
21
- acc[key] = undefined;
22
- }
23
- }
24
- return acc;
25
- }, {});
26
- }
10
+ await build(args.configPath, args.nodeExternals, args.dangerouslyUseUnsupportedNextVersion);
27
11
  function printHelp() {
28
12
  console.log(`Unknown command
29
13
 
@@ -33,6 +17,8 @@ You can use a custom config path here
33
17
  npx open-next build --config-path ./path/to/open-next.config.ts
34
18
  You can configure externals for the esbuild compilation of the open-next.config.ts file
35
19
  npx open-next build --node-externals aws-sdk,sharp,sqlite3
20
+ You can build with a Next.js version that is not supported by the Next.js team anymore
21
+ npx open-next build --dangerously-use-unsupported-next-version
36
22
  `);
37
23
  process.exit(1);
38
24
  }
@@ -1,6 +1,7 @@
1
1
  import { Readable, Writable } from "node:stream";
2
2
  import zlib from "node:zlib";
3
3
  import { error } from "../../adapters/logger";
4
+ import { getBrotliQuality } from "../../utils/compression";
4
5
  import { formatWarmerResponse } from "./aws-lambda";
5
6
  const handler = async (handler, converter) => async (event) => {
6
7
  // Handle warmer event
@@ -70,10 +71,7 @@ function compressBody(body, encoding) {
70
71
  case "br":
71
72
  transform = zlib.createBrotliCompress({
72
73
  params: {
73
- // This is a compromise between speed and compression ratio.
74
- // The default one will most likely timeout an AWS Lambda with default configuration on large bodies (>6mb).
75
- // Therefore we set it to 6, which is a good compromise.
76
- [zlib.constants.BROTLI_PARAM_QUALITY]: Number(process.env.BROTLI_QUALITY) ?? 6,
74
+ [zlib.constants.BROTLI_PARAM_QUALITY]: getBrotliQuality(),
77
75
  },
78
76
  });
79
77
  break;
@@ -1,6 +1,7 @@
1
1
  import { Readable } from "node:stream";
2
2
  import zlib from "node:zlib";
3
3
  import { debug, error } from "../../adapters/logger";
4
+ import { getBrotliQuality } from "../../utils/compression";
4
5
  import { DetachedPromiseRunner } from "../../utils/promise";
5
6
  function formatWarmerResponse(event) {
6
7
  const result = new Promise((resolve) => {
@@ -34,6 +35,9 @@ const handler = async (handler, converter) => awslambda.streamifyResponse(async
34
35
  compressedStream = zlib.createBrotliCompress({
35
36
  flush: zlib.constants.BROTLI_OPERATION_FLUSH,
36
37
  finishFlush: zlib.constants.BROTLI_OPERATION_FINISH,
38
+ params: {
39
+ [zlib.constants.BROTLI_PARAM_QUALITY]: getBrotliQuality(),
40
+ },
37
41
  });
38
42
  compressedStream.pipe(responseStream);
39
43
  }
@@ -0,0 +1,20 @@
1
+ export interface CliArgs {
2
+ /** Value of `--config-path` */
3
+ configPath?: string;
4
+ /** Value of `--node-externals` */
5
+ nodeExternals?: string;
6
+ /** Whether `--help` is present */
7
+ help: boolean;
8
+ /** Whether `--dangerously-use-unsupported-next-version` is present */
9
+ dangerouslyUseUnsupportedNextVersion: boolean;
10
+ }
11
+ /**
12
+ * Parses the command line arguments of the `open-next` CLI.
13
+ *
14
+ * Boolean flags (i.e. `--help`) are `true` when present and never consume the following argument.
15
+ * Other flags take the following argument as their value unless it starts with `-`.
16
+ *
17
+ * @param argv - The CLI arguments, without the node executable and script path (i.e. `process.argv.slice(2)`)
18
+ * @returns The parsed arguments
19
+ */
20
+ export declare function parseArgs(argv: string[]): CliArgs;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Flags that do not take a value. They are `true` when present and never consume the next argument.
3
+ */
4
+ const BOOLEAN_FLAGS = new Set([
5
+ "--help",
6
+ "--dangerously-use-unsupported-next-version",
7
+ ]);
8
+ /**
9
+ * Parses the command line arguments of the `open-next` CLI.
10
+ *
11
+ * Boolean flags (i.e. `--help`) are `true` when present and never consume the following argument.
12
+ * Other flags take the following argument as their value unless it starts with `-`.
13
+ *
14
+ * @param argv - The CLI arguments, without the node executable and script path (i.e. `process.argv.slice(2)`)
15
+ * @returns The parsed arguments
16
+ */
17
+ export function parseArgs(argv) {
18
+ const flags = new Set();
19
+ const values = {};
20
+ for (let i = 0; i < argv.length; i++) {
21
+ const key = argv[i];
22
+ if (!key.startsWith("--")) {
23
+ continue;
24
+ }
25
+ if (BOOLEAN_FLAGS.has(key)) {
26
+ flags.add(key);
27
+ continue;
28
+ }
29
+ const next = argv[i + 1];
30
+ if (next && !next.startsWith("-")) {
31
+ values[key] = next;
32
+ i++;
33
+ }
34
+ else {
35
+ values[key] = undefined;
36
+ }
37
+ }
38
+ return {
39
+ configPath: values["--config-path"],
40
+ nodeExternals: values["--node-externals"],
41
+ help: flags.has("--help"),
42
+ dangerouslyUseUnsupportedNextVersion: flags.has("--dangerously-use-unsupported-next-version"),
43
+ };
44
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Brotli quality used by the compressing wrappers.
3
+ *
4
+ * It can be set with the `BROTLI_QUALITY` environment variable (0 to 11) and defaults to 6.
5
+ * Quality 10 and 11 cost an order of magnitude more CPU than 6 for a few percent smaller output,
6
+ * which delays every streamed response.
7
+ */
8
+ export declare function getBrotliQuality(): number;
@@ -0,0 +1,16 @@
1
+ import zlib from "node:zlib";
2
+ const DEFAULT_BROTLI_QUALITY = 6;
3
+ /**
4
+ * Brotli quality used by the compressing wrappers.
5
+ *
6
+ * It can be set with the `BROTLI_QUALITY` environment variable (0 to 11) and defaults to 6.
7
+ * Quality 10 and 11 cost an order of magnitude more CPU than 6 for a few percent smaller output,
8
+ * which delays every streamed response.
9
+ */
10
+ export function getBrotliQuality() {
11
+ const quality = Number.parseInt(process.env.BROTLI_QUALITY ?? "", 10);
12
+ return quality >= zlib.constants.BROTLI_MIN_QUALITY &&
13
+ quality <= zlib.constants.BROTLI_MAX_QUALITY
14
+ ? quality
15
+ : DEFAULT_BROTLI_QUALITY;
16
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "access": "public"
4
4
  },
5
5
  "name": "@opennextjs/aws",
6
- "version": "4.1.11",
6
+ "version": "4.1.13",
7
7
  "bin": {
8
8
  "open-next": "./dist/index.js"
9
9
  },
@@ -56,13 +56,20 @@
56
56
  "@types/express": "5.0.6",
57
57
  "@types/node": "20.17.6",
58
58
  "concurrently": "^9.2.4",
59
+ "diff": "^9.0.0",
59
60
  "next": "16.4.1",
60
61
  "tsc-alias": "^1.9.7",
61
62
  "typescript": "5.9.3"
62
63
  },
63
64
  "peerDependencies": {
65
+ "diff": "^9.0.0",
64
66
  "next": ">=15.5.28 <16 || >=16.4.1"
65
67
  },
68
+ "peerDependenciesMeta": {
69
+ "diff": {
70
+ "optional": true
71
+ }
72
+ },
66
73
  "bugs": {
67
74
  "url": "https://github.com/opennextjs/opennextjs-aws/issues"
68
75
  },