@opennextjs/aws 3.2.0 → 3.2.1
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/dist/adapters/cache.d.ts +1 -0
- package/dist/adapters/cache.js +12 -6
- package/dist/build/compileConfig.js +2 -2
- package/dist/build/copyTracedFiles.js +2 -2
- package/dist/build/createImageOptimizationBundle.js +3 -0
- package/dist/build/createMiddleware.d.ts +4 -4
- package/dist/build/createMiddleware.js +9 -17
- package/dist/build/createServerBundle.js +11 -16
- package/dist/build/edge/createEdgeBundle.d.ts +4 -6
- package/dist/build/edge/createEdgeBundle.js +8 -10
- package/dist/build/installDeps.js +11 -1
- package/dist/core/createGenericHandler.js +5 -5
- package/dist/core/createMainHandler.js +5 -5
- package/dist/core/requestHandler.js +1 -1
- package/dist/core/routing/matcher.js +12 -5
- package/dist/core/routing/middleware.js +6 -3
- package/dist/core/routing/util.js +7 -4
- package/dist/core/routingHandler.js +1 -1
- package/dist/overrides/converters/edge.d.ts +3 -0
- package/dist/overrides/converters/edge.js +5 -2
- package/dist/overrides/wrappers/cloudflare.js +1 -1
- package/dist/plugins/edge.d.ts +3 -2
- package/dist/plugins/edge.js +27 -29
- package/dist/plugins/replacement.d.ts +1 -1
- package/dist/plugins/replacement.js +4 -5
- package/dist/types/open-next.d.ts +9 -4
- package/package.json +1 -1
- package/dist/adapters/plugins/without-routing/requestHandler.d.ts +0 -2
- package/dist/adapters/plugins/without-routing/requestHandler.js +0 -10
package/dist/adapters/cache.d.ts
CHANGED
|
@@ -88,6 +88,7 @@ export default class S3Cache {
|
|
|
88
88
|
kindHint?: "app" | "pages" | "fetch";
|
|
89
89
|
tags?: string[];
|
|
90
90
|
softTags?: string[];
|
|
91
|
+
kind?: "FETCH";
|
|
91
92
|
}): Promise<CacheHandlerValue | null>;
|
|
92
93
|
getFetchCache(key: string, softTags?: string[], tags?: string[]): Promise<CacheHandlerValue | null>;
|
|
93
94
|
getIncrementalCache(key: string): Promise<CacheHandlerValue | null>;
|
package/dist/adapters/cache.js
CHANGED
|
@@ -5,6 +5,17 @@ const CACHE_EXTENSION_REGEX = /\.(cache|fetch)$/;
|
|
|
5
5
|
export function hasCacheExtension(key) {
|
|
6
6
|
return CACHE_EXTENSION_REGEX.test(key);
|
|
7
7
|
}
|
|
8
|
+
function isFetchCache(options) {
|
|
9
|
+
if (typeof options === "boolean") {
|
|
10
|
+
return options;
|
|
11
|
+
}
|
|
12
|
+
if (typeof options === "object") {
|
|
13
|
+
return (options.kindHint === "fetch" ||
|
|
14
|
+
options.fetchCache ||
|
|
15
|
+
options.kind === "FETCH");
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
8
19
|
// We need to use globalThis client here as this class can be defined at load time in next 12 but client is not available at load time
|
|
9
20
|
export default class S3Cache {
|
|
10
21
|
constructor(_ctx) { }
|
|
@@ -14,14 +25,9 @@ export default class S3Cache {
|
|
|
14
25
|
if (globalThis.disableIncrementalCache) {
|
|
15
26
|
return null;
|
|
16
27
|
}
|
|
17
|
-
const isFetchCache = typeof options === "object"
|
|
18
|
-
? options.kindHint
|
|
19
|
-
? options.kindHint === "fetch"
|
|
20
|
-
: options.fetchCache
|
|
21
|
-
: options;
|
|
22
28
|
const softTags = typeof options === "object" ? options.softTags : [];
|
|
23
29
|
const tags = typeof options === "object" ? options.tags : [];
|
|
24
|
-
return isFetchCache
|
|
30
|
+
return isFetchCache(options)
|
|
25
31
|
? this.getFetchCache(key, softTags, tags)
|
|
26
32
|
: this.getIncrementalCache(key);
|
|
27
33
|
}
|
|
@@ -42,6 +42,7 @@ export async function compileOpenNextConfig(baseDir, openNextConfigPath, nodeExt
|
|
|
42
42
|
}
|
|
43
43
|
export function compileOpenNextConfigNode(sourcePath, outputDir, externals) {
|
|
44
44
|
const outputPath = path.join(outputDir, "open-next.config.mjs");
|
|
45
|
+
logger.debug("Compiling open-next.config.ts for Node.", outputPath);
|
|
45
46
|
//Check if open-next.config.ts exists
|
|
46
47
|
if (!fs.existsSync(sourcePath)) {
|
|
47
48
|
//Create a simple open-next.config.mjs file
|
|
@@ -71,7 +72,7 @@ export function compileOpenNextConfigNode(sourcePath, outputDir, externals) {
|
|
|
71
72
|
}
|
|
72
73
|
export function compileOpenNextConfigEdge(sourcePath, outputDir, externals) {
|
|
73
74
|
const outputPath = path.join(outputDir, "open-next.config.edge.mjs");
|
|
74
|
-
logger.
|
|
75
|
+
logger.debug("Compiling open-next.config.ts for edge runtime.", outputPath);
|
|
75
76
|
buildSync({
|
|
76
77
|
entryPoints: [sourcePath],
|
|
77
78
|
outfile: outputPath,
|
|
@@ -82,5 +83,4 @@ export function compileOpenNextConfigEdge(sourcePath, outputDir, externals) {
|
|
|
82
83
|
platform: "browser",
|
|
83
84
|
external: externals,
|
|
84
85
|
});
|
|
85
|
-
logger.info("Compiled open-next.config.ts for edge runtime.");
|
|
86
86
|
}
|
|
@@ -54,7 +54,7 @@ export async function copyTracedFiles(buildOutputPath, packagePath, outputDir, r
|
|
|
54
54
|
throw new Error(`
|
|
55
55
|
--------------------------------------------------------------------------------
|
|
56
56
|
${pagePath} cannot use the edge runtime.
|
|
57
|
-
OpenNext requires edge runtime function to be defined in a separate function.
|
|
57
|
+
OpenNext requires edge runtime function to be defined in a separate function.
|
|
58
58
|
See the docs for more information on how to bundle edge runtime functions.
|
|
59
59
|
--------------------------------------------------------------------------------
|
|
60
60
|
`);
|
|
@@ -71,7 +71,7 @@ File ${fullFilePath} does not exist
|
|
|
71
71
|
extractFiles(requiredFiles.files, path.join(standaloneNextDir, dir)).forEach((f) => {
|
|
72
72
|
filesToCopy.set(f, f.replace(standaloneDir, outputDir));
|
|
73
73
|
});
|
|
74
|
-
if (!existsSync(path.join(standaloneNextDir,
|
|
74
|
+
if (!existsSync(path.join(standaloneNextDir, fullFilePath))) {
|
|
75
75
|
throw new Error(`This error should only happen for static 404 and 500 page from page router. Report this if that's not the case.,
|
|
76
76
|
File ${fullFilePath} does not exist`);
|
|
77
77
|
}
|
|
@@ -75,5 +75,8 @@ export async function createImageOptimizationBundle(options) {
|
|
|
75
75
|
const sharpVersion = process.env.SHARP_VERSION ?? "0.32.6";
|
|
76
76
|
installDependencies(outputPath, config.imageOptimization?.install ?? {
|
|
77
77
|
packages: [`sharp@${sharpVersion}`],
|
|
78
|
+
arch: "arm64",
|
|
79
|
+
nodeVersion: "18",
|
|
80
|
+
libc: "glibc",
|
|
78
81
|
});
|
|
79
82
|
}
|
|
@@ -3,8 +3,8 @@ import * as buildHelper from "./helper.js";
|
|
|
3
3
|
* Compiles the middleware bundle.
|
|
4
4
|
*
|
|
5
5
|
* @param options Build Options.
|
|
6
|
-
* @
|
|
6
|
+
* @param forceOnlyBuildOnce force to build only once.
|
|
7
7
|
*/
|
|
8
|
-
export declare function createMiddleware(options: buildHelper.BuildOptions
|
|
9
|
-
|
|
10
|
-
}>;
|
|
8
|
+
export declare function createMiddleware(options: buildHelper.BuildOptions, { forceOnlyBuildOnce }?: {
|
|
9
|
+
forceOnlyBuildOnce?: boolean | undefined;
|
|
10
|
+
}): Promise<void>;
|
|
@@ -8,26 +8,16 @@ import { installDependencies } from "./installDeps.js";
|
|
|
8
8
|
* Compiles the middleware bundle.
|
|
9
9
|
*
|
|
10
10
|
* @param options Build Options.
|
|
11
|
-
* @
|
|
11
|
+
* @param forceOnlyBuildOnce force to build only once.
|
|
12
12
|
*/
|
|
13
|
-
export async function createMiddleware(options) {
|
|
13
|
+
export async function createMiddleware(options, { forceOnlyBuildOnce = false } = {}) {
|
|
14
14
|
logger.info(`Bundling middleware function...`);
|
|
15
15
|
const { appBuildOutputPath, config, outputDir } = options;
|
|
16
16
|
// Get middleware manifest
|
|
17
17
|
const middlewareManifest = JSON.parse(fs.readFileSync(path.join(appBuildOutputPath, ".next/server/middleware-manifest.json"), "utf8"));
|
|
18
|
-
const
|
|
19
|
-
if (!entry) {
|
|
20
|
-
return { useMiddleware: false };
|
|
21
|
-
}
|
|
22
|
-
// Create output folder
|
|
23
|
-
let outputPath = path.join(outputDir, "server-function");
|
|
24
|
-
const commonMiddlewareOptions = {
|
|
25
|
-
middlewareInfo: entry,
|
|
26
|
-
options,
|
|
27
|
-
appBuildOutputPath,
|
|
28
|
-
};
|
|
18
|
+
const middlewareInfo = middlewareManifest.middleware["/"];
|
|
29
19
|
if (config.middleware?.external) {
|
|
30
|
-
outputPath = path.join(outputDir, "middleware");
|
|
20
|
+
const outputPath = path.join(outputDir, "middleware");
|
|
31
21
|
fs.mkdirSync(outputPath, { recursive: true });
|
|
32
22
|
// Copy open-next.config.mjs
|
|
33
23
|
buildHelper.copyOpenNextConfig(options.buildDir, outputPath, config.middleware.override?.wrapper === "cloudflare");
|
|
@@ -35,11 +25,13 @@ export async function createMiddleware(options) {
|
|
|
35
25
|
await buildEdgeBundle({
|
|
36
26
|
entrypoint: path.join(options.openNextDistDir, "adapters", "middleware.js"),
|
|
37
27
|
outfile: path.join(outputPath, "handler.mjs"),
|
|
38
|
-
|
|
28
|
+
middlewareInfo,
|
|
29
|
+
options,
|
|
39
30
|
overrides: config.middleware?.override,
|
|
40
31
|
defaultConverter: "aws-cloudfront",
|
|
41
32
|
includeCache: config.dangerous?.enableCacheInterception,
|
|
42
33
|
additionalExternals: config.edgeExternals,
|
|
34
|
+
onlyBuildOnce: forceOnlyBuildOnce === true,
|
|
43
35
|
});
|
|
44
36
|
installDependencies(outputPath, config.middleware?.install);
|
|
45
37
|
}
|
|
@@ -47,9 +39,9 @@ export async function createMiddleware(options) {
|
|
|
47
39
|
await buildEdgeBundle({
|
|
48
40
|
entrypoint: path.join(options.openNextDistDir, "core", "edgeFunctionHandler.js"),
|
|
49
41
|
outfile: path.join(options.buildDir, "middleware.mjs"),
|
|
50
|
-
|
|
42
|
+
middlewareInfo,
|
|
43
|
+
options,
|
|
51
44
|
onlyBuildOnce: true,
|
|
52
45
|
});
|
|
53
46
|
}
|
|
54
|
-
return { useMiddleware: true };
|
|
55
47
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import logger from "../logger.js";
|
|
5
4
|
import { minifyAll } from "../minimize-js.js";
|
|
@@ -11,7 +10,6 @@ import { copyTracedFiles } from "./copyTracedFiles.js";
|
|
|
11
10
|
import { generateEdgeBundle } from "./edge/createEdgeBundle.js";
|
|
12
11
|
import * as buildHelper from "./helper.js";
|
|
13
12
|
import { installDependencies } from "./installDeps.js";
|
|
14
|
-
const require = createRequire(import.meta.url);
|
|
15
13
|
export async function createServerBundle(options) {
|
|
16
14
|
const { config } = options;
|
|
17
15
|
const foundRoutes = new Set();
|
|
@@ -27,17 +25,17 @@ export async function createServerBundle(options) {
|
|
|
27
25
|
const routes = fnOptions.routes;
|
|
28
26
|
routes.forEach((route) => foundRoutes.add(route));
|
|
29
27
|
if (fnOptions.runtime === "edge") {
|
|
30
|
-
await generateEdgeBundle(name,
|
|
28
|
+
await generateEdgeBundle(name, options, fnOptions);
|
|
31
29
|
}
|
|
32
30
|
else {
|
|
33
|
-
await generateBundle(name,
|
|
31
|
+
await generateBundle(name, options, fnOptions);
|
|
34
32
|
}
|
|
35
33
|
});
|
|
36
34
|
//TODO: throw an error if not all edge runtime routes has been bundled in a separate function
|
|
37
35
|
// We build every other function than default before so we know which route there is left
|
|
38
36
|
await Promise.all(promises);
|
|
39
37
|
const remainingRoutes = new Set();
|
|
40
|
-
const {
|
|
38
|
+
const { appBuildOutputPath, monorepoRoot } = options;
|
|
41
39
|
const packagePath = path.relative(monorepoRoot, appBuildOutputPath);
|
|
42
40
|
// Find remaining routes
|
|
43
41
|
const serverPath = path.join(appBuildOutputPath, ".next", "standalone", packagePath, ".next", "server");
|
|
@@ -68,15 +66,15 @@ export async function createServerBundle(options) {
|
|
|
68
66
|
}, () => { });
|
|
69
67
|
}
|
|
70
68
|
// Generate default function
|
|
71
|
-
await generateBundle("default",
|
|
69
|
+
await generateBundle("default", options, {
|
|
72
70
|
...defaultFn,
|
|
73
71
|
// @ts-expect-error - Those string are RouteTemplate
|
|
74
72
|
routes: Array.from(remainingRoutes),
|
|
75
73
|
patterns: ["*"],
|
|
76
74
|
});
|
|
77
75
|
}
|
|
78
|
-
async function generateBundle(name,
|
|
79
|
-
const { appPath, appBuildOutputPath, outputDir, monorepoRoot } = options;
|
|
76
|
+
async function generateBundle(name, options, fnOptions) {
|
|
77
|
+
const { appPath, appBuildOutputPath, config, outputDir, monorepoRoot } = options;
|
|
80
78
|
logger.info(`Building server function: ${name}...`);
|
|
81
79
|
// Create output folder
|
|
82
80
|
const outputPath = path.join(outputDir, "server-functions", name);
|
|
@@ -99,8 +97,7 @@ async function generateBundle(name, config, options, fnOptions) {
|
|
|
99
97
|
await bundleNextServer(path.join(outputPath, packagePath), appPath);
|
|
100
98
|
}
|
|
101
99
|
// Copy middleware
|
|
102
|
-
if (!config.middleware?.external
|
|
103
|
-
fs.existsSync(path.join(options.buildDir, "middleware.mjs"))) {
|
|
100
|
+
if (!config.middleware?.external) {
|
|
104
101
|
fs.copyFileSync(path.join(options.buildDir, "middleware.mjs"), path.join(outputPath, packagePath, "middleware.mjs"));
|
|
105
102
|
}
|
|
106
103
|
// Copy open-next.config.mjs
|
|
@@ -123,12 +120,10 @@ async function generateBundle(name, config, options, fnOptions) {
|
|
|
123
120
|
openNextReplacementPlugin({
|
|
124
121
|
name: `requestHandlerOverride ${name}`,
|
|
125
122
|
target: /core(\/|\\)requestHandler\.js/g,
|
|
126
|
-
deletes:
|
|
127
|
-
|
|
128
|
-
? [
|
|
129
|
-
|
|
130
|
-
]
|
|
131
|
-
: [],
|
|
123
|
+
deletes: [
|
|
124
|
+
...(disableNextPrebundledReact ? ["applyNextjsPrebundledReact"] : []),
|
|
125
|
+
...(disableRouting ? ["withRouting"] : []),
|
|
126
|
+
],
|
|
132
127
|
}),
|
|
133
128
|
openNextReplacementPlugin({
|
|
134
129
|
name: `utilOverride ${name}`,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { MiddlewareInfo } from "../../types/next-types";
|
|
2
|
-
import type { IncludedConverter,
|
|
2
|
+
import type { IncludedConverter, OverrideOptions, SplittedFunctionOptions } from "../../types/open-next";
|
|
3
3
|
import type { BuildOptions } from "../helper.js";
|
|
4
4
|
interface BuildEdgeBundleOptions {
|
|
5
|
-
|
|
6
|
-
middlewareInfo: MiddlewareInfo;
|
|
5
|
+
middlewareInfo?: MiddlewareInfo;
|
|
7
6
|
entrypoint: string;
|
|
8
7
|
outfile: string;
|
|
9
8
|
options: BuildOptions;
|
|
@@ -14,7 +13,6 @@ interface BuildEdgeBundleOptions {
|
|
|
14
13
|
additionalExternals?: string[];
|
|
15
14
|
onlyBuildOnce?: boolean;
|
|
16
15
|
}
|
|
17
|
-
export declare function buildEdgeBundle({
|
|
18
|
-
export declare function
|
|
19
|
-
export declare function generateEdgeBundle(name: string, config: OpenNextConfig, options: BuildOptions, fnOptions: SplittedFunctionOptions): Promise<void>;
|
|
16
|
+
export declare function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, options, defaultConverter, overrides, additionalInject, includeCache, additionalExternals, onlyBuildOnce, }: BuildEdgeBundleOptions): Promise<void>;
|
|
17
|
+
export declare function generateEdgeBundle(name: string, options: BuildOptions, fnOptions: SplittedFunctionOptions): Promise<void>;
|
|
20
18
|
export {};
|
|
@@ -7,7 +7,7 @@ import { openNextEdgePlugins } from "../../plugins/edge.js";
|
|
|
7
7
|
import { openNextReplacementPlugin } from "../../plugins/replacement.js";
|
|
8
8
|
import { openNextResolvePlugin } from "../../plugins/resolve.js";
|
|
9
9
|
import { copyOpenNextConfig, esbuildAsync } from "../helper.js";
|
|
10
|
-
export async function buildEdgeBundle({
|
|
10
|
+
export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, options, defaultConverter, overrides, additionalInject, includeCache, additionalExternals, onlyBuildOnce, }) {
|
|
11
11
|
const isInCloudfare = typeof overrides?.wrapper === "string"
|
|
12
12
|
? overrides.wrapper === "cloudflare"
|
|
13
13
|
: (await overrides?.wrapper?.())?.edgeRuntime;
|
|
@@ -50,7 +50,7 @@ export async function buildEdgeBundle({ appBuildOutputPath, middlewareInfo, entr
|
|
|
50
50
|
}),
|
|
51
51
|
openNextEdgePlugins({
|
|
52
52
|
middlewareInfo,
|
|
53
|
-
nextDir: path.join(appBuildOutputPath, ".next"),
|
|
53
|
+
nextDir: path.join(options.appBuildOutputPath, ".next"),
|
|
54
54
|
edgeFunctionHandlerPath: path.join(options.openNextDistDir, "core", "edgeFunctionHandler.js"),
|
|
55
55
|
isInCloudfare,
|
|
56
56
|
}),
|
|
@@ -106,8 +106,7 @@ globalThis.AsyncLocalStorage = AsyncLocalStorage;
|
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
export function
|
|
110
|
-
export async function generateEdgeBundle(name, config, options, fnOptions) {
|
|
109
|
+
export async function generateEdgeBundle(name, options, fnOptions) {
|
|
111
110
|
const { appBuildOutputPath, outputDir } = options;
|
|
112
111
|
logger.info(`Generating edge bundle for: ${name}`);
|
|
113
112
|
// Create output folder
|
|
@@ -122,26 +121,25 @@ export async function generateEdgeBundle(name, config, options, fnOptions) {
|
|
|
122
121
|
if (functions.length > 1) {
|
|
123
122
|
throw new Error("Only one function is supported for now");
|
|
124
123
|
}
|
|
125
|
-
const
|
|
124
|
+
const middlewareInfo = functions[0];
|
|
126
125
|
//Copy wasm files
|
|
127
|
-
const wasmFiles =
|
|
126
|
+
const wasmFiles = middlewareInfo.wasm;
|
|
128
127
|
mkdirSync(path.join(outputPath, "wasm"), { recursive: true });
|
|
129
128
|
for (const wasmFile of wasmFiles) {
|
|
130
129
|
fs.copyFileSync(path.join(appBuildOutputPath, ".next", wasmFile.filePath), path.join(outputPath, `wasm/${wasmFile.name}.wasm`));
|
|
131
130
|
}
|
|
132
131
|
// Copy assets
|
|
133
|
-
const assets =
|
|
132
|
+
const assets = middlewareInfo.assets;
|
|
134
133
|
mkdirSync(path.join(outputPath, "assets"), { recursive: true });
|
|
135
134
|
for (const asset of assets) {
|
|
136
135
|
fs.copyFileSync(path.join(appBuildOutputPath, ".next", asset.filePath), path.join(outputPath, `assets/${asset.name}`));
|
|
137
136
|
}
|
|
138
137
|
await buildEdgeBundle({
|
|
139
|
-
|
|
140
|
-
middlewareInfo: fn,
|
|
138
|
+
middlewareInfo,
|
|
141
139
|
entrypoint: path.join(options.openNextDistDir, "adapters", "edge-adapter.js"),
|
|
142
140
|
outfile: path.join(outputPath, "index.mjs"),
|
|
143
141
|
options,
|
|
144
142
|
overrides: fnOptions.override,
|
|
145
|
-
additionalExternals: config.edgeExternals,
|
|
143
|
+
additionalExternals: options.config.edgeExternals,
|
|
146
144
|
});
|
|
147
145
|
}
|
|
@@ -14,7 +14,17 @@ export function installDependencies(outputDir, installOptions) {
|
|
|
14
14
|
logger.info(`Installing dependencies for ${name}...`);
|
|
15
15
|
// We then need to run install in the tempDir
|
|
16
16
|
// 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
|
|
17
|
-
const
|
|
17
|
+
const archOption = installOptions.arch
|
|
18
|
+
? `--arch=${installOptions.arch}`
|
|
19
|
+
: "";
|
|
20
|
+
const targetOption = installOptions.nodeVersion
|
|
21
|
+
? `--target=${installOptions.nodeVersion}`
|
|
22
|
+
: "";
|
|
23
|
+
const libcOption = installOptions.libc
|
|
24
|
+
? `--libc=${installOptions.libc}`
|
|
25
|
+
: "";
|
|
26
|
+
const additionalArgs = installOptions.additionalArgs ?? "";
|
|
27
|
+
const installCommand = `npm install --platform=linux ${archOption} ${targetOption} ${libcOption} ${additionalArgs} ${installOptions.packages.join(" ")}`;
|
|
18
28
|
execSync(installCommand, {
|
|
19
29
|
stdio: "pipe",
|
|
20
30
|
cwd: tempInstallDir,
|
|
@@ -7,10 +7,10 @@ export async function createGenericHandler(handler) {
|
|
|
7
7
|
globalThis.openNextConfig = config;
|
|
8
8
|
const override = config[handler.type]
|
|
9
9
|
?.override;
|
|
10
|
-
// From the config, we create the
|
|
11
|
-
const
|
|
10
|
+
// From the config, we create the converter
|
|
11
|
+
const converter = await resolveConverter(override?.converter);
|
|
12
12
|
// Then we create the handler
|
|
13
|
-
const wrapper = await resolveWrapper(override?.wrapper);
|
|
14
|
-
debug("Using wrapper",
|
|
15
|
-
return wrapper
|
|
13
|
+
const { name, wrapper } = await resolveWrapper(override?.wrapper);
|
|
14
|
+
debug("Using wrapper", name);
|
|
15
|
+
return wrapper(handler.handler, converter);
|
|
16
16
|
}
|
|
@@ -15,10 +15,10 @@ export async function createMainHandler() {
|
|
|
15
15
|
globalThis.incrementalCache = await resolveIncrementalCache(thisFunction.override?.incrementalCache);
|
|
16
16
|
globalThis.tagCache = await resolveTagCache(thisFunction.override?.tagCache);
|
|
17
17
|
globalThis.lastModified = {};
|
|
18
|
-
// From the config, we create the
|
|
19
|
-
const
|
|
18
|
+
// From the config, we create the converter
|
|
19
|
+
const converter = await resolveConverter(thisFunction.override?.converter);
|
|
20
20
|
// Then we create the handler
|
|
21
|
-
const wrapper = await resolveWrapper(thisFunction.override?.wrapper);
|
|
22
|
-
debug("Using wrapper",
|
|
23
|
-
return wrapper
|
|
21
|
+
const { wrapper, name } = await resolveWrapper(thisFunction.override?.wrapper);
|
|
22
|
+
debug("Using wrapper", name);
|
|
23
|
+
return wrapper(openNextHandler, converter);
|
|
24
24
|
}
|
|
@@ -14,13 +14,13 @@ export async function openNextHandler(internalEvent, responseStreaming) {
|
|
|
14
14
|
internalEvent.headers.host = internalEvent.headers["x-forwarded-host"];
|
|
15
15
|
}
|
|
16
16
|
debug("internalEvent", internalEvent);
|
|
17
|
-
//#override withRouting
|
|
18
17
|
let preprocessResult = {
|
|
19
18
|
internalEvent: internalEvent,
|
|
20
19
|
isExternalRewrite: false,
|
|
21
20
|
origin: false,
|
|
22
21
|
isISR: false,
|
|
23
22
|
};
|
|
23
|
+
//#override withRouting
|
|
24
24
|
try {
|
|
25
25
|
preprocessResult = await routingHandler(internalEvent);
|
|
26
26
|
}
|
|
@@ -122,10 +122,16 @@ export function handleRewrites(event, rewrites) {
|
|
|
122
122
|
const { pathname, protocol, hostname, queryString } = getUrlParts(rewrite.destination, isExternalRewrite);
|
|
123
123
|
// We need to use a localized path if the rewrite is not locale specific
|
|
124
124
|
const pathToUse = rewrite.locale === false ? rawPath : localizedRawPath;
|
|
125
|
+
// We need to encode the "+" character with its UTF-8 equivalent "%20" to avoid 2 issues:
|
|
126
|
+
// 1. The compile function from path-to-regexp will throw an error if it finds a "+" character.
|
|
127
|
+
// https://github.com/pillarjs/path-to-regexp?tab=readme-ov-file#unexpected--or-
|
|
128
|
+
// 2. The convertToQueryString function will replace the "+" character with %2B instead of %20.
|
|
129
|
+
// %2B does not get interpreted as a space in the URL thus breaking the query string.
|
|
130
|
+
const encodePlusQueryString = queryString.replaceAll("+", "%20");
|
|
125
131
|
debug("urlParts", { pathname, protocol, hostname, queryString });
|
|
126
132
|
const toDestinationPath = compile(escapeRegex(pathname ?? "") ?? "");
|
|
127
133
|
const toDestinationHost = compile(escapeRegex(hostname ?? "") ?? "");
|
|
128
|
-
const toDestinationQuery = compile(escapeRegex(
|
|
134
|
+
const toDestinationQuery = compile(escapeRegex(encodePlusQueryString ?? "") ?? "");
|
|
129
135
|
let params = {
|
|
130
136
|
// params for the source
|
|
131
137
|
...getParamsFromSource(match(escapeRegex(rewrite?.source) ?? ""))(pathToUse),
|
|
@@ -139,7 +145,7 @@ export function handleRewrites(event, rewrites) {
|
|
|
139
145
|
}, {}),
|
|
140
146
|
};
|
|
141
147
|
const isUsingParams = Object.keys(params).length > 0;
|
|
142
|
-
let rewrittenQuery =
|
|
148
|
+
let rewrittenQuery = encodePlusQueryString;
|
|
143
149
|
let rewrittenHost = hostname;
|
|
144
150
|
let rewrittenPath = pathname;
|
|
145
151
|
if (isUsingParams) {
|
|
@@ -149,7 +155,7 @@ export function handleRewrites(event, rewrites) {
|
|
|
149
155
|
}
|
|
150
156
|
rewrittenUrl = isExternalRewrite
|
|
151
157
|
? `${protocol}//${rewrittenHost}${rewrittenPath}`
|
|
152
|
-
:
|
|
158
|
+
: `${rewrittenPath}`;
|
|
153
159
|
// Should we merge the query params or use only the ones from the rewrite?
|
|
154
160
|
finalQuery = {
|
|
155
161
|
...query,
|
|
@@ -244,8 +250,9 @@ export function fixDataPage(internalEvent, buildId) {
|
|
|
244
250
|
};
|
|
245
251
|
}
|
|
246
252
|
if (rawPath.startsWith(dataPattern) && rawPath.endsWith(".json")) {
|
|
247
|
-
|
|
248
|
-
|
|
253
|
+
const newPath = rawPath
|
|
254
|
+
.slice(dataPattern.length, -".json".length)
|
|
255
|
+
.replace(/^\/index$/, "/");
|
|
249
256
|
query.__nextDataReq = "1";
|
|
250
257
|
return {
|
|
251
258
|
...internalEvent,
|
|
@@ -30,13 +30,16 @@ export async function handleMiddleware(internalEvent, middlewareLoader = default
|
|
|
30
30
|
// We bypass the middleware if the request is internal
|
|
31
31
|
if (internalEvent.headers["x-isr"])
|
|
32
32
|
return internalEvent;
|
|
33
|
+
// Retrieve the protocol:
|
|
34
|
+
// - In lambda, the url only contains the rawPath and the query - default to https
|
|
35
|
+
// - In cloudflare, the protocol is usually http in dev and https in production
|
|
36
|
+
const protocol = internalEvent.url.startsWith("http://") ? "http:" : "https:";
|
|
33
37
|
const host = internalEvent.headers.host
|
|
34
|
-
?
|
|
38
|
+
? `${protocol}//${internalEvent.headers.host}`
|
|
35
39
|
: "http://localhost:3000";
|
|
36
40
|
const initialUrl = new URL(normalizedPath, host);
|
|
37
41
|
initialUrl.search = convertToQueryString(query);
|
|
38
42
|
const url = initialUrl.toString();
|
|
39
|
-
// console.log("url", url, normalizedPath);
|
|
40
43
|
const middleware = await middlewareLoader();
|
|
41
44
|
const result = await middleware.default({
|
|
42
45
|
geo: {
|
|
@@ -96,7 +99,7 @@ export async function handleMiddleware(internalEvent, middlewareLoader = default
|
|
|
96
99
|
resHeaders.location =
|
|
97
100
|
responseHeaders
|
|
98
101
|
.get("location")
|
|
99
|
-
?.replace("http://localhost:3000",
|
|
102
|
+
?.replace("http://localhost:3000", `${protocol}//${internalEvent.headers.host}`) ?? resHeaders.location;
|
|
100
103
|
// res.setHeader("Location", location);
|
|
101
104
|
return {
|
|
102
105
|
body: emptyReadableStream(),
|
|
@@ -37,7 +37,7 @@ export function getUrlParts(url, isExternal) {
|
|
|
37
37
|
const match = url.match(regex);
|
|
38
38
|
return {
|
|
39
39
|
hostname: "",
|
|
40
|
-
pathname: match?.[1]
|
|
40
|
+
pathname: match?.[1] ? `/${match[1]}` : url,
|
|
41
41
|
protocol: "",
|
|
42
42
|
queryString: match?.[2] ?? "",
|
|
43
43
|
};
|
|
@@ -50,7 +50,7 @@ export function getUrlParts(url, isExternal) {
|
|
|
50
50
|
return {
|
|
51
51
|
protocol: match[1] ?? "https:",
|
|
52
52
|
hostname: match[2],
|
|
53
|
-
pathname: match[3],
|
|
53
|
+
pathname: match[3] ?? "",
|
|
54
54
|
queryString: match[4]?.slice(1) ?? "",
|
|
55
55
|
};
|
|
56
56
|
}
|
|
@@ -84,13 +84,16 @@ export function convertRes(res) {
|
|
|
84
84
|
* @__PURE__
|
|
85
85
|
*/
|
|
86
86
|
export function convertToQueryString(query) {
|
|
87
|
+
// URLSearchParams is a representation of the PARSED query.
|
|
88
|
+
// So we must decode the value before appending it to the URLSearchParams.
|
|
89
|
+
// https://stackoverflow.com/a/45516812
|
|
87
90
|
const urlQuery = new URLSearchParams();
|
|
88
91
|
Object.entries(query).forEach(([key, value]) => {
|
|
89
92
|
if (Array.isArray(value)) {
|
|
90
|
-
value.forEach((entry) => urlQuery.append(key, entry));
|
|
93
|
+
value.forEach((entry) => urlQuery.append(key, decodeURIComponent(entry)));
|
|
91
94
|
}
|
|
92
95
|
else {
|
|
93
|
-
urlQuery.append(key, value);
|
|
96
|
+
urlQuery.append(key, decodeURIComponent(value));
|
|
94
97
|
}
|
|
95
98
|
});
|
|
96
99
|
const queryString = urlQuery.toString();
|
|
@@ -30,7 +30,7 @@ function applyMiddlewareHeaders(eventHeaders, middlewareHeaders, setPrefix = tru
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
export default async function routingHandler(event) {
|
|
33
|
-
const nextHeaders = addNextConfigHeaders(event, ConfigHeaders)
|
|
33
|
+
const nextHeaders = addNextConfigHeaders(event, ConfigHeaders);
|
|
34
34
|
let internalEvent = fixDataPage(event, BuildId);
|
|
35
35
|
if ("statusCode" in internalEvent) {
|
|
36
36
|
return internalEvent;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { Converter, InternalEvent, InternalResult } from "../../types/open-next";
|
|
2
2
|
import type { MiddlewareOutputEvent } from "../../core/routingHandler";
|
|
3
|
+
declare global {
|
|
4
|
+
var __dangerous_ON_edge_converter_returns_request: boolean | undefined;
|
|
5
|
+
}
|
|
3
6
|
declare const converter: Converter<InternalEvent, InternalResult | ({
|
|
4
7
|
type: "middleware";
|
|
5
8
|
} & MiddlewareOutputEvent)>;
|
|
@@ -52,7 +52,7 @@ const converter = {
|
|
|
52
52
|
url = `https://${result.internalEvent.headers.host}${url}`;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
const
|
|
55
|
+
const request = new Request(url, {
|
|
56
56
|
body: result.internalEvent.body,
|
|
57
57
|
method: result.internalEvent.method,
|
|
58
58
|
headers: {
|
|
@@ -60,12 +60,15 @@ const converter = {
|
|
|
60
60
|
"x-forwarded-host": result.internalEvent.headers.host,
|
|
61
61
|
},
|
|
62
62
|
});
|
|
63
|
+
if (globalThis.__dangerous_ON_edge_converter_returns_request === true) {
|
|
64
|
+
return request;
|
|
65
|
+
}
|
|
63
66
|
const cfCache = (result.isISR ||
|
|
64
67
|
result.internalEvent.rawPath.startsWith("/_next/image")) &&
|
|
65
68
|
process.env.DISABLE_CACHE !== "true"
|
|
66
69
|
? { cacheEverything: true }
|
|
67
70
|
: {};
|
|
68
|
-
return fetch(
|
|
71
|
+
return fetch(request, {
|
|
69
72
|
// This is a hack to make sure that the response is cached by Cloudflare
|
|
70
73
|
// See https://developers.cloudflare.com/workers/examples/cache-using-fetch/#caching-html-resources
|
|
71
74
|
// @ts-expect-error - This is a Cloudflare specific option
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const handler = async (handler, converter) => async (event, env) => {
|
|
2
2
|
globalThis.process = process;
|
|
3
3
|
// Set the environment variables
|
|
4
|
-
//
|
|
4
|
+
// Cloudflare suggests to not override the process.env object but instead apply the values to it
|
|
5
5
|
for (const [key, value] of Object.entries(env)) {
|
|
6
6
|
if (typeof value === "string") {
|
|
7
7
|
process.env[key] = value;
|
package/dist/plugins/edge.d.ts
CHANGED
|
@@ -3,13 +3,14 @@ import type { MiddlewareInfo } from "../types/next-types.js";
|
|
|
3
3
|
export interface IPluginSettings {
|
|
4
4
|
nextDir: string;
|
|
5
5
|
edgeFunctionHandlerPath?: string;
|
|
6
|
-
middlewareInfo
|
|
6
|
+
middlewareInfo?: MiddlewareInfo;
|
|
7
7
|
isInCloudfare?: boolean;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* @param opts.nextDir - The path to the .next directory
|
|
11
11
|
* @param opts.edgeFunctionHandlerPath - The path to the edgeFunctionHandler.js file that we'll use to bundle the routing
|
|
12
|
-
* @param opts.
|
|
12
|
+
* @param opts.middlewareInfo - Information about the middleware
|
|
13
|
+
* @param opts.isInCloudfare - Whether the code runs on the cloudflare runtime
|
|
13
14
|
* @returns
|
|
14
15
|
*/
|
|
15
16
|
export declare function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewareInfo, isInCloudfare, }: IPluginSettings): Plugin;
|
package/dist/plugins/edge.js
CHANGED
|
@@ -4,19 +4,22 @@ import { loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, l
|
|
|
4
4
|
/**
|
|
5
5
|
* @param opts.nextDir - The path to the .next directory
|
|
6
6
|
* @param opts.edgeFunctionHandlerPath - The path to the edgeFunctionHandler.js file that we'll use to bundle the routing
|
|
7
|
-
* @param opts.
|
|
7
|
+
* @param opts.middlewareInfo - Information about the middleware
|
|
8
|
+
* @param opts.isInCloudfare - Whether the code runs on the cloudflare runtime
|
|
8
9
|
* @returns
|
|
9
10
|
*/
|
|
10
11
|
export function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewareInfo, isInCloudfare, }) {
|
|
11
|
-
const entryFiles = middlewareInfo
|
|
12
|
-
const routes =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
const entryFiles = middlewareInfo?.files.map((file) => path.join(nextDir, file)) ?? [];
|
|
13
|
+
const routes = middlewareInfo
|
|
14
|
+
? [
|
|
15
|
+
{
|
|
16
|
+
name: middlewareInfo.name || "/",
|
|
17
|
+
page: middlewareInfo.page,
|
|
18
|
+
regex: middlewareInfo.matchers.map((m) => m.regexp),
|
|
19
|
+
},
|
|
20
|
+
]
|
|
21
|
+
: [];
|
|
22
|
+
const wasmFiles = middlewareInfo?.wasm ?? [];
|
|
20
23
|
return {
|
|
21
24
|
name: "opennext-edge",
|
|
22
25
|
setup(build) {
|
|
@@ -44,12 +47,10 @@ export function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewa
|
|
|
44
47
|
});
|
|
45
48
|
// we convert the imports we tagged with the node-built-in-modules namespace so that instead of `require("node:*")`
|
|
46
49
|
// they import from `export * from "node:*";`
|
|
47
|
-
build.onLoad({ filter: /.*/, namespace: "node-built-in-modules" }, ({ path }) => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
};
|
|
52
|
-
});
|
|
50
|
+
build.onLoad({ filter: /.*/, namespace: "node-built-in-modules" }, ({ path }) => ({
|
|
51
|
+
contents: `export * from '${path}'`,
|
|
52
|
+
loader: "js",
|
|
53
|
+
}));
|
|
53
54
|
// We inject the entry files into the edgeFunctionHandler
|
|
54
55
|
build.onLoad({ filter: /\/edgeFunctionHandler.js/g }, async (args) => {
|
|
55
56
|
let contents = readFileSync(args.path, "utf-8");
|
|
@@ -97,7 +98,7 @@ if(!globalThis.Crypto) {
|
|
|
97
98
|
globalThis.Crypto = webcrypto.Crypto
|
|
98
99
|
}
|
|
99
100
|
// We also need to polyfill URLPattern
|
|
100
|
-
if (!globalThis.URLPattern) {
|
|
101
|
+
if (!globalThis.URLPattern) {
|
|
101
102
|
await import("urlpattern-polyfill");
|
|
102
103
|
}
|
|
103
104
|
`}
|
|
@@ -106,8 +107,8 @@ ${wasmFiles
|
|
|
106
107
|
? `import ${file.name} from './wasm/${file.name}.wasm';`
|
|
107
108
|
: `const ${file.name} = readFileSync(path.join(__dirname,'/wasm/${file.name}.wasm'));`)
|
|
108
109
|
.join("\n")}
|
|
109
|
-
${entryFiles
|
|
110
|
-
${contents}
|
|
110
|
+
${entryFiles.map((file) => `require("${file}");`).join("\n")}
|
|
111
|
+
${contents}
|
|
111
112
|
`;
|
|
112
113
|
return {
|
|
113
114
|
contents,
|
|
@@ -124,18 +125,18 @@ ${contents}
|
|
|
124
125
|
const MiddlewareManifest = loadMiddlewareManifest(nextDir);
|
|
125
126
|
const contents = `
|
|
126
127
|
import path from "path";
|
|
127
|
-
|
|
128
|
+
|
|
128
129
|
import { debug } from "../logger";
|
|
129
|
-
|
|
130
|
+
|
|
130
131
|
if(!globalThis.__dirname) {
|
|
131
132
|
globalThis.__dirname = ""
|
|
132
133
|
}
|
|
133
|
-
|
|
134
|
+
|
|
134
135
|
export const NEXT_DIR = path.join(__dirname, ".next");
|
|
135
136
|
export const OPEN_NEXT_DIR = path.join(__dirname, ".open-next");
|
|
136
|
-
|
|
137
|
+
|
|
137
138
|
debug({ NEXT_DIR, OPEN_NEXT_DIR });
|
|
138
|
-
|
|
139
|
+
|
|
139
140
|
export const NextConfig = ${JSON.stringify(NextConfig)};
|
|
140
141
|
export const BuildId = ${JSON.stringify(BuildId)};
|
|
141
142
|
export const HtmlPages = ${JSON.stringify(HtmlPages)};
|
|
@@ -146,11 +147,8 @@ ${contents}
|
|
|
146
147
|
export const MiddlewareManifest = ${JSON.stringify(MiddlewareManifest)};
|
|
147
148
|
|
|
148
149
|
process.env.NEXT_BUILD_ID = BuildId;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
contents,
|
|
153
|
-
};
|
|
150
|
+
`;
|
|
151
|
+
return { contents };
|
|
154
152
|
});
|
|
155
153
|
},
|
|
156
154
|
};
|
|
@@ -11,7 +11,7 @@ const importPattern = /\/\/#import([\s\S]*?)\n\/\/#endImport/gm;
|
|
|
11
11
|
* deletes: ["id1"],
|
|
12
12
|
* })
|
|
13
13
|
*
|
|
14
|
-
* To inject
|
|
14
|
+
* To inject arbitrary code by using (import at top of file):
|
|
15
15
|
*
|
|
16
16
|
* //#import
|
|
17
17
|
*
|
|
@@ -49,9 +49,8 @@ export function openNextReplacementPlugin({ target, replacements, deletes, name,
|
|
|
49
49
|
logger.debug(chalk.blue(`Open-next replacement plugin ${name}`), ` -- Deleting override for ${id}`);
|
|
50
50
|
contents = contents.replace(pattern, "");
|
|
51
51
|
}),
|
|
52
|
-
...(replacements ?? []).map(async (
|
|
53
|
-
const
|
|
54
|
-
const replacementFile = await readFile(p, "utf-8");
|
|
52
|
+
...(replacements ?? []).map(async (filename) => {
|
|
53
|
+
const replacementFile = await readFile(filename, "utf-8");
|
|
55
54
|
const matches = replacementFile.matchAll(overridePattern);
|
|
56
55
|
const importMatch = replacementFile.match(importPattern);
|
|
57
56
|
const addedImport = importMatch ? importMatch[0] : "";
|
|
@@ -60,7 +59,7 @@ export function openNextReplacementPlugin({ target, replacements, deletes, name,
|
|
|
60
59
|
const replacement = match[2];
|
|
61
60
|
const id = match[1];
|
|
62
61
|
const pattern = new RegExp(`\/\/#override (${id})\n([\\s\\S]*?)\/\/#endOverride`, "g");
|
|
63
|
-
logger.debug(chalk.blue(`Open-next replacement plugin ${name}`), `-- Applying override for ${id} from ${
|
|
62
|
+
logger.debug(chalk.blue(`Open-next replacement plugin ${name}`), `-- Applying override for ${id} from ${filename}`);
|
|
64
63
|
contents = contents.replace(pattern, replacement);
|
|
65
64
|
}
|
|
66
65
|
}),
|
|
@@ -143,17 +143,22 @@ export interface InstallOptions {
|
|
|
143
143
|
*/
|
|
144
144
|
packages: string[];
|
|
145
145
|
/**
|
|
146
|
-
* @default
|
|
146
|
+
* @default undefined
|
|
147
147
|
*/
|
|
148
148
|
arch?: "x64" | "arm64";
|
|
149
149
|
/**
|
|
150
|
-
* @default
|
|
150
|
+
* @default undefined
|
|
151
151
|
*/
|
|
152
|
-
nodeVersion?:
|
|
152
|
+
nodeVersion?: string;
|
|
153
153
|
/**
|
|
154
|
-
* @default
|
|
154
|
+
* @default undefined
|
|
155
155
|
*/
|
|
156
156
|
libc?: "glibc" | "musl";
|
|
157
|
+
/**
|
|
158
|
+
* @default undefined
|
|
159
|
+
* Additional arguments to pass to the install command (i.e. npm install)
|
|
160
|
+
*/
|
|
161
|
+
additionalArgs?: string;
|
|
157
162
|
}
|
|
158
163
|
export interface DefaultFunctionOptions<E extends BaseEventOrResult = InternalEvent, R extends BaseEventOrResult = InternalResult> {
|
|
159
164
|
/**
|
package/package.json
CHANGED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
//#override withRouting
|
|
2
|
-
const preprocessResult = {
|
|
3
|
-
internalEvent: internalEvent,
|
|
4
|
-
isExternalRewrite: false,
|
|
5
|
-
origin: false,
|
|
6
|
-
isISR: false,
|
|
7
|
-
};
|
|
8
|
-
//#endOverride
|
|
9
|
-
// We need to export something otherwise when compiled in js it creates an empty export {} inside the override
|
|
10
|
-
export default {};
|