@opennextjs/aws 3.6.2 → 3.6.3

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.
package/README.md CHANGED
@@ -96,6 +96,8 @@ To run `OpenNext` locally:
96
96
  path/to/opennextjs-aws/packages/open-next/dist/index.js build
97
97
  ```
98
98
 
99
+ There is also a way to run OpenNext locally. You can read a guide about it [here](https://opennext.js.org/aws/contribute/local_run). Its mostly used for development/debugging purposes.
100
+
99
101
  ### Coldstart
100
102
 
101
103
  OpenNext provide you with a warmer function that can be used to reduce cold start.
@@ -23,6 +23,7 @@ export declare const RoutesManifest: {
23
23
  };
24
24
  export declare const ConfigHeaders: import("../../types/next-types").Header[] | undefined;
25
25
  export declare const PrerenderManifest: import("../../types/next-types").PrerenderManifest;
26
+ export declare const PagesManifest: Record<string, string>;
26
27
  export declare const AppPathsManifestKeys: string[];
27
28
  export declare const MiddlewareManifest: import("../../types/next-types").MiddlewareManifest;
28
29
  export declare const AppPathsManifest: Record<string, string>;
@@ -1,10 +1,9 @@
1
1
  import path from "node:path";
2
2
  import { debug } from "../logger";
3
- import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadFunctionsConfigManifest, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "./util.js";
3
+ import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadFunctionsConfigManifest, loadHtmlPages, loadMiddlewareManifest, loadPagesManifest, loadPrerenderManifest, loadRoutesManifest, } from "./util.js";
4
4
  export const NEXT_DIR = path.join(__dirname, ".next");
5
5
  export const OPEN_NEXT_DIR = path.join(__dirname, ".open-next");
6
6
  debug({ NEXT_DIR, OPEN_NEXT_DIR });
7
- //TODO: inject these values at build time
8
7
  export const NextConfig = /* @__PURE__ */ loadConfig(NEXT_DIR);
9
8
  export const BuildId = /* @__PURE__ */ loadBuildId(NEXT_DIR);
10
9
  export const HtmlPages = /* @__PURE__ */ loadHtmlPages(NEXT_DIR);
@@ -13,6 +12,7 @@ export const RoutesManifest = /* @__PURE__ */ loadRoutesManifest(NEXT_DIR);
13
12
  export const ConfigHeaders = /* @__PURE__ */ loadConfigHeaders(NEXT_DIR);
14
13
  export const PrerenderManifest =
15
14
  /* @__PURE__ */ loadPrerenderManifest(NEXT_DIR);
15
+ export const PagesManifest = /* @__PURE__ */ loadPagesManifest(NEXT_DIR);
16
16
  export const AppPathsManifestKeys =
17
17
  /* @__PURE__ */ loadAppPathsManifestKeys(NEXT_DIR);
18
18
  export const MiddlewareManifest =
@@ -2,7 +2,7 @@ import type { FunctionsConfigManifest, MiddlewareManifest, NextConfig, Prerender
2
2
  import type { PublicFiles } from "../../build";
3
3
  export declare function loadConfig(nextDir: string): NextConfig;
4
4
  export declare function loadBuildId(nextDir: string): string;
5
- export declare function loadPagesManifest(nextDir: string): any;
5
+ export declare function loadPagesManifest(nextDir: string): Record<string, string>;
6
6
  export declare function loadHtmlPages(nextDir: string): string[];
7
7
  export declare function loadPublicAssets(openNextDir: string): PublicFiles;
8
8
  export declare function loadRoutesManifest(nextDir: string): {
@@ -10,7 +10,7 @@ export declare function getManifests(nextDir: string): {
10
10
  buildId: string;
11
11
  config: import("../types/next-types.js").NextConfig;
12
12
  prerenderManifest: import("../types/next-types.js").PrerenderManifest;
13
- pagesManifest: any;
13
+ pagesManifest: Record<string, string>;
14
14
  appPathsManifest: Record<string, string>;
15
15
  middlewareManifest: import("../types/next-types.js").MiddlewareManifest;
16
16
  functionsConfigManifest: import("../types/next-types.js").FunctionsConfigManifest;
@@ -22,7 +22,7 @@ export declare function copyTracedFiles({ buildOutputPath, packagePath, outputDi
22
22
  buildId: string;
23
23
  config: import("../types/next-types.js").NextConfig;
24
24
  prerenderManifest: import("../types/next-types.js").PrerenderManifest;
25
- pagesManifest: any;
25
+ pagesManifest: Record<string, string>;
26
26
  appPathsManifest: Record<string, string>;
27
27
  middlewareManifest: import("../types/next-types.js").MiddlewareManifest;
28
28
  functionsConfigManifest: import("../types/next-types.js").FunctionsConfigManifest;
@@ -302,7 +302,11 @@ export function handleFallbackFalse(internalEvent, prerenderManifest) {
302
302
  ? rawPath
303
303
  : `/${NextConfig.i18n?.defaultLocale}${rawPath}`;
304
304
  // We need to remove the trailing slash if it exists
305
- if (NextConfig.trailingSlash && localizedPath.endsWith("/")) {
305
+ if (
306
+ // Not if localizedPath is "/" tho, because that would not make it find `isPregenerated` below since it would be try to match an empty string.
307
+ localizedPath !== "/" &&
308
+ NextConfig.trailingSlash &&
309
+ localizedPath.endsWith("/")) {
306
310
  localizedPath = localizedPath.slice(0, -1);
307
311
  }
308
312
  const matchedStaticRoute = staticRouteMatcher(localizedPath);
@@ -1,4 +1,3 @@
1
1
  import type { ResolvedRoute } from "../../types/open-next";
2
- export declare const apiPrefix: string;
3
2
  export declare const staticRouteMatcher: (path: string) => ResolvedRoute[];
4
3
  export declare const dynamicRouteMatcher: (path: string) => ResolvedRoute[];
@@ -1,12 +1,10 @@
1
- import { AppPathRoutesManifest, RoutesManifest } from "../../adapters/config/index";
1
+ import { AppPathRoutesManifest, PagesManifest, RoutesManifest, } from "../../adapters/config/index";
2
2
  // Add the locale prefix to the regex so we correctly match the rawPath
3
3
  const optionalLocalePrefixRegex = `^/(?:${RoutesManifest.locales.map((locale) => `${locale}/?`).join("|")})?`;
4
4
  // Add the basepath prefix to the regex so we correctly match the rawPath
5
5
  const optionalBasepathPrefixRegex = RoutesManifest.basePath
6
6
  ? `^${RoutesManifest.basePath}/?`
7
7
  : "^/";
8
- // Add the basePath prefix to the api routes
9
- export const apiPrefix = `${RoutesManifest.basePath ?? ""}/api`;
10
8
  const optionalPrefix = optionalLocalePrefixRegex.replace("^/", optionalBasepathPrefixRegex);
11
9
  function routeMatcher(routeDefinitions) {
12
10
  const regexp = routeDefinitions.map((route) => ({
@@ -41,5 +39,32 @@ function routeMatcher(routeDefinitions) {
41
39
  });
42
40
  };
43
41
  }
44
- export const staticRouteMatcher = routeMatcher(RoutesManifest.routes.static);
42
+ export const staticRouteMatcher = routeMatcher([
43
+ ...RoutesManifest.routes.static,
44
+ ...getStaticAPIRoutes(),
45
+ ]);
45
46
  export const dynamicRouteMatcher = routeMatcher(RoutesManifest.routes.dynamic);
47
+ /**
48
+ * Returns static API routes for both app and pages router cause Next will filter them out in staticRoutes in `routes-manifest.json`.
49
+ * We also need to filter out page files that are under `app/api/*` as those would not be present in the routes manifest either.
50
+ * This line from Next.js skips it:
51
+ * https://github.com/vercel/next.js/blob/ded56f952154a40dcfe53bdb38c73174e9eca9e5/packages/next/src/build/index.ts#L1299
52
+ *
53
+ * Without it handleFallbackFalse will 404 on static API routes if there is a catch-all route on root level.
54
+ */
55
+ function getStaticAPIRoutes() {
56
+ const createRouteDefinition = (route) => ({
57
+ page: route,
58
+ regex: `^${route}(?:/)?$`,
59
+ });
60
+ const dynamicRoutePages = new Set(RoutesManifest.routes.dynamic.map(({ page }) => page));
61
+ const pagesStaticAPIRoutes = Object.keys(PagesManifest)
62
+ .filter((route) => route.startsWith("/api/") && !dynamicRoutePages.has(route))
63
+ .map(createRouteDefinition);
64
+ // We filter out both static API and page routes from the app paths manifest
65
+ const appPathsStaticAPIRoutes = Object.values(AppPathRoutesManifest)
66
+ .filter((route) => route.startsWith("/api/") ||
67
+ (route === "/api" && !dynamicRoutePages.has(route)))
68
+ .map(createRouteDefinition);
69
+ return [...pagesStaticAPIRoutes, ...appPathsStaticAPIRoutes];
70
+ }
@@ -4,7 +4,7 @@ import { cacheInterceptor } from "./routing/cacheInterceptor";
4
4
  import { detectLocale } from "./routing/i18n";
5
5
  import { fixDataPage, getNextConfigHeaders, handleFallbackFalse, handleRedirects, handleRewrites, } from "./routing/matcher";
6
6
  import { handleMiddleware } from "./routing/middleware";
7
- import { apiPrefix, dynamicRouteMatcher, staticRouteMatcher, } from "./routing/routeMatcher";
7
+ import { dynamicRouteMatcher, staticRouteMatcher, } from "./routing/routeMatcher";
8
8
  import { constructNextUrl } from "./routing/util";
9
9
  export const MIDDLEWARE_HEADER_PREFIX = "x-middleware-response-";
10
10
  export const MIDDLEWARE_HEADER_PREFIX_LEN = MIDDLEWARE_HEADER_PREFIX.length;
@@ -55,6 +55,9 @@ export default async function routingHandler(event) {
55
55
  }
56
56
  const redirect = handleRedirects(internalEvent, RoutesManifest.redirects);
57
57
  if (redirect) {
58
+ // We need to encode the value in the Location header to make sure it is valid according to RFC
59
+ // https://stackoverflow.com/a/7654605/16587222
60
+ redirect.headers.Location = new URL(redirect.headers.Location).href;
58
61
  debug("redirect", redirect);
59
62
  return redirect;
60
63
  }
@@ -85,9 +88,14 @@ export default async function routingHandler(event) {
85
88
  internalEvent = afterRewrites.internalEvent;
86
89
  isExternalRewrite = afterRewrites.isExternalRewrite;
87
90
  }
91
+ let isISR = false;
88
92
  // We want to run this just before the dynamic route check
89
- const { event: fallbackEvent, isISR } = handleFallbackFalse(internalEvent, PrerenderManifest);
90
- internalEvent = fallbackEvent;
93
+ // We can skip it if its an external rewrite
94
+ if (!isExternalRewrite) {
95
+ const fallbackResult = handleFallbackFalse(internalEvent, PrerenderManifest);
96
+ internalEvent = fallbackResult.event;
97
+ isISR = fallbackResult.isISR;
98
+ }
91
99
  const foundDynamicRoute = dynamicRouteMatcher(internalEvent.rawPath);
92
100
  const isDynamicRoute = !isExternalRewrite && foundDynamicRoute.length > 0;
93
101
  if (!(isDynamicRoute || isStaticRoute || isExternalRewrite)) {
@@ -96,17 +104,11 @@ export default async function routingHandler(event) {
96
104
  internalEvent = fallbackRewrites.internalEvent;
97
105
  isExternalRewrite = fallbackRewrites.isExternalRewrite;
98
106
  }
99
- // Api routes are not present in the routes manifest except if they're not behind /api
100
- // /api even if it's a page route doesn't get generated in the manifest
101
- // Ideally we would need to properly check api routes here
102
- const isApiRoute = internalEvent.rawPath === apiPrefix ||
103
- internalEvent.rawPath.startsWith(`${apiPrefix}/`);
104
107
  const isNextImageRoute = internalEvent.rawPath.startsWith("/_next/image");
105
108
  const isRouteFoundBeforeAllRewrites = isStaticRoute || isDynamicRoute || isExternalRewrite;
106
109
  // If we still haven't found a route, we show the 404 page
107
110
  // We need to ensure that rewrites are applied before showing the 404 page
108
111
  if (!(isRouteFoundBeforeAllRewrites ||
109
- isApiRoute ||
110
112
  isNextImageRoute ||
111
113
  // We need to check again once all rewrites have been applied
112
114
  staticRouteMatcher(internalEvent.rawPath).length > 0 ||
@@ -1,10 +1,14 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { NextConfig } from "../../adapters/config/index";
3
4
  import { getMonorepoRelativePath } from "../../utils/normalize-path";
4
5
  export default {
5
6
  name: "fs-dev",
6
7
  load: async (url) => {
7
- const imagePath = path.join(getMonorepoRelativePath(), "assets", url);
8
+ const urlWithoutBasePath = NextConfig.basePath
9
+ ? url.slice(NextConfig.basePath.length)
10
+ : url;
11
+ const imagePath = path.join(getMonorepoRelativePath(), "assets", urlWithoutBasePath);
8
12
  const body = fs.createReadStream(imagePath);
9
13
  const contentType = url.endsWith(".png") ? "image/png" : "image/jpeg";
10
14
  return {
@@ -1,13 +1,15 @@
1
1
  import path from "node:path";
2
2
  import express from "express";
3
+ import { NextConfig } from "../../adapters/config/index";
3
4
  import { getMonorepoRelativePath } from "../../utils/normalize-path";
4
5
  const wrapper = async (handler, converter) => {
5
6
  const app = express();
6
7
  // To serve static assets
7
- app.use(express.static(path.join(getMonorepoRelativePath(), "assets")));
8
+ const basePath = NextConfig.basePath ?? "";
9
+ app.use(basePath, express.static(path.join(getMonorepoRelativePath(), "assets")));
8
10
  const imageHandlerPath = path.join(getMonorepoRelativePath(), "image-optimization-function/index.mjs");
9
11
  const imageHandler = await import(imageHandlerPath).then((m) => m.handler);
10
- app.all("/_next/image", async (req, res) => {
12
+ app.all(`${NextConfig.basePath ?? ""}/_next/image`, async (req, res) => {
11
13
  const internalEvent = await converter.convertFrom(req);
12
14
  const streamCreator = {
13
15
  writeHeaders: (prelude) => {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "access": "public"
4
4
  },
5
5
  "name": "@opennextjs/aws",
6
- "version": "3.6.2",
6
+ "version": "3.6.3",
7
7
  "bin": {
8
8
  "open-next": "./dist/index.js"
9
9
  },