@opennextjs/aws 3.5.1 → 3.5.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/dist/adapters/config/util.d.ts +1 -0
- package/dist/adapters/config/util.js +5 -2
- package/dist/adapters/logger.js +1 -1
- package/dist/build/compileCache.js +1 -1
- package/dist/build/constant.d.ts +1 -0
- package/dist/build/constant.js +1 -0
- package/dist/build/copyTracedFiles.d.ts +21 -1
- package/dist/build/copyTracedFiles.js +39 -7
- package/dist/build/createImageOptimizationBundle.js +1 -1
- package/dist/build/createServerBundle.d.ts +9 -1
- package/dist/build/createServerBundle.js +28 -8
- package/dist/build/edge/createEdgeBundle.d.ts +5 -2
- package/dist/build/edge/createEdgeBundle.js +11 -2
- package/dist/build/helper.d.ts +3 -2
- package/dist/build/helper.js +44 -19
- package/dist/build/patch/astCodePatcher.d.ts +58 -0
- package/dist/build/patch/astCodePatcher.js +76 -0
- package/dist/build/patch/codePatcher.d.ts +46 -0
- package/dist/build/patch/codePatcher.js +119 -0
- package/dist/build/patch/patchFetchCacheISR.d.ts +5 -0
- package/dist/build/patch/patchFetchCacheISR.js +102 -0
- package/dist/build/patch/patchFetchCacheWaitUntil.d.ts +3 -0
- package/dist/build/patch/patchFetchCacheWaitUntil.js +36 -0
- package/dist/core/requestHandler.js +3 -1
- package/dist/core/routing/cacheInterceptor.js +6 -1
- package/dist/core/routing/matcher.d.ts +1 -1
- package/dist/core/routing/matcher.js +9 -1
- package/dist/core/routing/middleware.js +6 -0
- package/dist/core/routing/queue.d.ts +1 -0
- package/dist/core/routing/queue.js +12 -8
- package/dist/core/routing/util.js +3 -3
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +2 -0
- package/dist/overrides/wrappers/express-dev.js +2 -0
- package/dist/plugins/content-updater.d.ts +48 -0
- package/dist/plugins/content-updater.js +65 -0
- package/dist/types/overrides.d.ts +2 -0
- package/package.json +4 -2
|
@@ -2,6 +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
6
|
export declare function loadHtmlPages(nextDir: string): string[];
|
|
6
7
|
export declare function loadPublicAssets(openNextDir: string): PublicFiles;
|
|
7
8
|
export declare function loadRoutesManifest(nextDir: string): {
|
|
@@ -10,10 +10,13 @@ export function loadBuildId(nextDir) {
|
|
|
10
10
|
const filePath = path.join(nextDir, "BUILD_ID");
|
|
11
11
|
return fs.readFileSync(filePath, "utf-8").trim();
|
|
12
12
|
}
|
|
13
|
-
export function
|
|
13
|
+
export function loadPagesManifest(nextDir) {
|
|
14
14
|
const filePath = path.join(nextDir, "server/pages-manifest.json");
|
|
15
15
|
const json = fs.readFileSync(filePath, "utf-8");
|
|
16
|
-
return
|
|
16
|
+
return JSON.parse(json);
|
|
17
|
+
}
|
|
18
|
+
export function loadHtmlPages(nextDir) {
|
|
19
|
+
return Object.entries(loadPagesManifest(nextDir))
|
|
17
20
|
.filter(([_, value]) => value.endsWith(".html"))
|
|
18
21
|
.map(([key]) => key);
|
|
19
22
|
}
|
package/dist/adapters/logger.js
CHANGED
|
@@ -11,7 +11,7 @@ export function compileCache(options, format = "cjs") {
|
|
|
11
11
|
const { config } = options;
|
|
12
12
|
const ext = format === "cjs" ? "cjs" : "mjs";
|
|
13
13
|
const outFile = path.join(options.buildDir, `cache.${ext}`);
|
|
14
|
-
const isAfter15 = buildHelper.compareSemver(options.nextVersion, "15.0.0")
|
|
14
|
+
const isAfter15 = buildHelper.compareSemver(options.nextVersion, ">=", "15.0.0");
|
|
15
15
|
buildHelper.esbuildSync({
|
|
16
16
|
external: ["next", "styled-jsx", "react", "@aws-sdk/*"],
|
|
17
17
|
entryPoints: [path.join(options.openNextDistDir, "adapters", "cache.js")],
|
package/dist/build/constant.d.ts
CHANGED
package/dist/build/constant.js
CHANGED
|
@@ -6,5 +6,25 @@ interface CopyTracedFilesOptions {
|
|
|
6
6
|
bundledNextServer: boolean;
|
|
7
7
|
skipServerFiles?: boolean;
|
|
8
8
|
}
|
|
9
|
-
export declare function
|
|
9
|
+
export declare function getManifests(nextDir: string): {
|
|
10
|
+
buildId: string;
|
|
11
|
+
config: import("../types/next-types.js").NextConfig;
|
|
12
|
+
prerenderManifest: import("../types/next-types.js").PrerenderManifest;
|
|
13
|
+
pagesManifest: any;
|
|
14
|
+
appPathsManifest: Record<string, string>;
|
|
15
|
+
middlewareManifest: import("../types/next-types.js").MiddlewareManifest;
|
|
16
|
+
functionsConfigManifest: import("../types/next-types.js").FunctionsConfigManifest;
|
|
17
|
+
};
|
|
18
|
+
export declare function copyTracedFiles({ buildOutputPath, packagePath, outputDir, routes, bundledNextServer, skipServerFiles, }: CopyTracedFilesOptions): Promise<{
|
|
19
|
+
tracedFiles: string[];
|
|
20
|
+
manifests: {
|
|
21
|
+
buildId: string;
|
|
22
|
+
config: import("../types/next-types.js").NextConfig;
|
|
23
|
+
prerenderManifest: import("../types/next-types.js").PrerenderManifest;
|
|
24
|
+
pagesManifest: any;
|
|
25
|
+
appPathsManifest: Record<string, string>;
|
|
26
|
+
middlewareManifest: import("../types/next-types.js").MiddlewareManifest;
|
|
27
|
+
functionsConfigManifest: import("../types/next-types.js").FunctionsConfigManifest;
|
|
28
|
+
};
|
|
29
|
+
}>;
|
|
10
30
|
export {};
|
|
@@ -1,15 +1,38 @@
|
|
|
1
1
|
import url from "node:url";
|
|
2
2
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, statSync, symlinkSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { loadConfig, loadPrerenderManifest } from "../adapters/config/util.js";
|
|
4
|
+
import { loadAppPathsManifest, loadBuildId, loadConfig, loadFunctionsConfigManifest, loadMiddlewareManifest, loadPagesManifest, loadPrerenderManifest, } from "../adapters/config/util.js";
|
|
5
|
+
import { getCrossPlatformPathRegex } from "../utils/regex.js";
|
|
5
6
|
import logger from "../logger.js";
|
|
6
|
-
import { MIDDLEWARE_TRACE_FILE } from "./constant.js";
|
|
7
|
+
import { INSTRUMENTATION_TRACE_FILE, MIDDLEWARE_TRACE_FILE, } from "./constant.js";
|
|
7
8
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
|
9
|
+
//TODO: we need to figure which packages we could safely remove
|
|
10
|
+
const EXCLUDED_PACKAGES = [
|
|
11
|
+
"caniuse-lite",
|
|
12
|
+
"sharp",
|
|
13
|
+
// This seems to be only in Next 15
|
|
14
|
+
// Some of sharp deps are under the @img scope
|
|
15
|
+
"@img",
|
|
16
|
+
];
|
|
17
|
+
function isExcluded(srcPath) {
|
|
18
|
+
return EXCLUDED_PACKAGES.some((excluded) => srcPath.match(getCrossPlatformPathRegex(`/node_modules/${excluded}/`)));
|
|
19
|
+
}
|
|
8
20
|
function copyPatchFile(outputDir) {
|
|
9
21
|
const patchFile = path.join(__dirname, "patch", "patchedAsyncStorage.js");
|
|
10
22
|
const outputPatchFile = path.join(outputDir, "patchedAsyncStorage.cjs");
|
|
11
23
|
copyFileSync(patchFile, outputPatchFile);
|
|
12
24
|
}
|
|
25
|
+
export function getManifests(nextDir) {
|
|
26
|
+
return {
|
|
27
|
+
buildId: loadBuildId(nextDir),
|
|
28
|
+
config: loadConfig(nextDir),
|
|
29
|
+
prerenderManifest: loadPrerenderManifest(nextDir),
|
|
30
|
+
pagesManifest: loadPagesManifest(nextDir),
|
|
31
|
+
appPathsManifest: loadAppPathsManifest(nextDir),
|
|
32
|
+
middlewareManifest: loadMiddlewareManifest(nextDir),
|
|
33
|
+
functionsConfigManifest: loadFunctionsConfigManifest(nextDir),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
13
36
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
14
37
|
export async function copyTracedFiles({ buildOutputPath, packagePath, outputDir, routes, bundledNextServer, skipServerFiles, }) {
|
|
15
38
|
const tsStart = Date.now();
|
|
@@ -88,10 +111,18 @@ File ${fullFilePath} does not exist
|
|
|
88
111
|
}
|
|
89
112
|
}
|
|
90
113
|
};
|
|
114
|
+
// Check for instrumentation trace file
|
|
115
|
+
if (existsSync(path.join(dotNextDir, INSTRUMENTATION_TRACE_FILE))) {
|
|
116
|
+
// We still need to copy the nft.json file so that computeCopyFilesForPage doesn't throw
|
|
117
|
+
copyFileSync(path.join(dotNextDir, INSTRUMENTATION_TRACE_FILE), path.join(standaloneNextDir, INSTRUMENTATION_TRACE_FILE));
|
|
118
|
+
computeCopyFilesForPage("instrumentation");
|
|
119
|
+
logger.debug("Adding instrumentation trace files");
|
|
120
|
+
}
|
|
91
121
|
if (existsSync(path.join(dotNextDir, MIDDLEWARE_TRACE_FILE))) {
|
|
92
122
|
// We still need to copy the nft.json file so that computeCopyFilesForPage doesn't throw
|
|
93
123
|
copyFileSync(path.join(dotNextDir, MIDDLEWARE_TRACE_FILE), path.join(standaloneNextDir, MIDDLEWARE_TRACE_FILE));
|
|
94
124
|
computeCopyFilesForPage("middleware");
|
|
125
|
+
logger.debug("Adding node middleware trace files");
|
|
95
126
|
}
|
|
96
127
|
const hasPageDir = routes.some((route) => route.startsWith("pages/"));
|
|
97
128
|
const hasAppDir = routes.some((route) => route.startsWith("app/"));
|
|
@@ -119,11 +150,8 @@ File ${fullFilePath} does not exist
|
|
|
119
150
|
});
|
|
120
151
|
//Actually copy the files
|
|
121
152
|
filesToCopy.forEach((to, from) => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
from.includes(path.join("node_modules", "caniuse-lite")) ||
|
|
125
|
-
// from.includes("jest-worker") || This ones seems necessary for next 12
|
|
126
|
-
from.includes(path.join("node_modules", "sharp"))) {
|
|
153
|
+
// We don't want to copy excluded packages (i.e sharp)
|
|
154
|
+
if (isExcluded(from)) {
|
|
127
155
|
return;
|
|
128
156
|
}
|
|
129
157
|
mkdirSync(path.dirname(to), { recursive: true });
|
|
@@ -204,4 +232,8 @@ File ${fullFilePath} does not exist
|
|
|
204
232
|
.forEach((file) => copyStaticFile(`server/${file}`));
|
|
205
233
|
}
|
|
206
234
|
logger.debug("copyTracedFiles:", Date.now() - tsStart, "ms");
|
|
235
|
+
return {
|
|
236
|
+
tracedFiles: Array.from(filesToCopy.values()),
|
|
237
|
+
manifests: getManifests(standaloneNextDir),
|
|
238
|
+
};
|
|
207
239
|
}
|
|
@@ -26,7 +26,7 @@ export async function createImageOptimizationBundle(options) {
|
|
|
26
26
|
},
|
|
27
27
|
}),
|
|
28
28
|
];
|
|
29
|
-
if (buildHelper.compareSemver(options.nextVersion, "14.1.1")
|
|
29
|
+
if (buildHelper.compareSemver(options.nextVersion, ">=", "14.1.1")) {
|
|
30
30
|
plugins.push(openNextReplacementPlugin({
|
|
31
31
|
name: "opennext-14.1.1-image-optimization",
|
|
32
32
|
target: getCrossPlatformPathRegex("plugins/image-optimization/image-optimization.js"),
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
import type { Plugin } from "esbuild";
|
|
2
|
+
import { ContentUpdater } from "../plugins/content-updater.js";
|
|
1
3
|
import * as buildHelper from "./helper.js";
|
|
2
|
-
|
|
4
|
+
import { type CodePatcher } from "./patch/codePatcher.js";
|
|
5
|
+
interface CodeCustomization {
|
|
6
|
+
additionalCodePatches?: CodePatcher[];
|
|
7
|
+
additionalPlugins?: (contentUpdater: ContentUpdater) => Plugin[];
|
|
8
|
+
}
|
|
9
|
+
export declare function createServerBundle(options: buildHelper.BuildOptions, codeCustomization?: CodeCustomization): Promise<void>;
|
|
10
|
+
export {};
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import logger from "../logger.js";
|
|
4
4
|
import { minifyAll } from "../minimize-js.js";
|
|
5
|
+
import { ContentUpdater } from "../plugins/content-updater.js";
|
|
5
6
|
import { openNextReplacementPlugin } from "../plugins/replacement.js";
|
|
6
7
|
import { openNextResolvePlugin } from "../plugins/resolve.js";
|
|
7
8
|
import { getCrossPlatformPathRegex } from "../utils/regex.js";
|
|
@@ -11,7 +12,10 @@ import { copyTracedFiles } from "./copyTracedFiles.js";
|
|
|
11
12
|
import { generateEdgeBundle } from "./edge/createEdgeBundle.js";
|
|
12
13
|
import * as buildHelper from "./helper.js";
|
|
13
14
|
import { installDependencies } from "./installDeps.js";
|
|
14
|
-
|
|
15
|
+
import { applyCodePatches } from "./patch/codePatcher.js";
|
|
16
|
+
import { patchFetchCacheForISR, patchUnstableCacheForISR, } from "./patch/patchFetchCacheISR.js";
|
|
17
|
+
import { patchFetchCacheSetMissingWaitUntil } from "./patch/patchFetchCacheWaitUntil.js";
|
|
18
|
+
export async function createServerBundle(options, codeCustomization) {
|
|
15
19
|
const { config } = options;
|
|
16
20
|
const foundRoutes = new Set();
|
|
17
21
|
// Get all functions to build
|
|
@@ -29,7 +33,7 @@ export async function createServerBundle(options) {
|
|
|
29
33
|
await generateEdgeBundle(name, options, fnOptions);
|
|
30
34
|
}
|
|
31
35
|
else {
|
|
32
|
-
await generateBundle(name, options, fnOptions);
|
|
36
|
+
await generateBundle(name, options, fnOptions, codeCustomization);
|
|
33
37
|
}
|
|
34
38
|
});
|
|
35
39
|
//TODO: throw an error if not all edge runtime routes has been bundled in a separate function
|
|
@@ -67,7 +71,7 @@ export async function createServerBundle(options) {
|
|
|
67
71
|
patterns: ["*"],
|
|
68
72
|
});
|
|
69
73
|
}
|
|
70
|
-
async function generateBundle(name, options, fnOptions) {
|
|
74
|
+
async function generateBundle(name, options, fnOptions, codeCustomization) {
|
|
71
75
|
const { appPath, appBuildOutputPath, config, outputDir, monorepoRoot } = options;
|
|
72
76
|
logger.info(`Building server function: ${name}...`);
|
|
73
77
|
// Create output folder
|
|
@@ -100,23 +104,35 @@ async function generateBundle(name, options, fnOptions) {
|
|
|
100
104
|
// Copy env files
|
|
101
105
|
buildHelper.copyEnvFile(appBuildOutputPath, packagePath, outputPath);
|
|
102
106
|
// Copy all necessary traced files
|
|
103
|
-
await copyTracedFiles({
|
|
107
|
+
const { tracedFiles, manifests } = await copyTracedFiles({
|
|
104
108
|
buildOutputPath: appBuildOutputPath,
|
|
105
109
|
packagePath,
|
|
106
110
|
outputDir: outputPath,
|
|
107
111
|
routes: fnOptions.routes ?? ["app/page.tsx"],
|
|
108
112
|
bundledNextServer: isBundled,
|
|
109
113
|
});
|
|
114
|
+
const additionalCodePatches = codeCustomization?.additionalCodePatches ?? [];
|
|
115
|
+
await applyCodePatches(options, tracedFiles, manifests, [
|
|
116
|
+
patchFetchCacheSetMissingWaitUntil,
|
|
117
|
+
patchFetchCacheForISR,
|
|
118
|
+
patchUnstableCacheForISR,
|
|
119
|
+
...additionalCodePatches,
|
|
120
|
+
]);
|
|
110
121
|
// Build Lambda code
|
|
111
122
|
// note: bundle in OpenNext package b/c the adapter relies on the
|
|
112
123
|
// "serverless-http" package which is not a dependency in user's
|
|
113
124
|
// Next.js app.
|
|
114
|
-
const disableNextPrebundledReact = buildHelper.compareSemver(options.nextVersion, "13.5.1")
|
|
115
|
-
buildHelper.compareSemver(options.nextVersion, "13.4.1")
|
|
125
|
+
const disableNextPrebundledReact = buildHelper.compareSemver(options.nextVersion, ">=", "13.5.1") ||
|
|
126
|
+
buildHelper.compareSemver(options.nextVersion, "<=", "13.4.1");
|
|
116
127
|
const overrides = fnOptions.override ?? {};
|
|
117
|
-
const isBefore13413 = buildHelper.compareSemver(options.nextVersion, "13.4.13")
|
|
118
|
-
const isAfter141 = buildHelper.compareSemver(options.nextVersion, "14.1")
|
|
128
|
+
const isBefore13413 = buildHelper.compareSemver(options.nextVersion, "<=", "13.4.13");
|
|
129
|
+
const isAfter141 = buildHelper.compareSemver(options.nextVersion, ">=", "14.1");
|
|
130
|
+
const isAfter142 = buildHelper.compareSemver(options.nextVersion, ">=", "14.2");
|
|
119
131
|
const disableRouting = isBefore13413 || config.middleware?.external;
|
|
132
|
+
const updater = new ContentUpdater(options);
|
|
133
|
+
const additionalPlugins = codeCustomization?.additionalPlugins
|
|
134
|
+
? codeCustomization.additionalPlugins(updater)
|
|
135
|
+
: [];
|
|
120
136
|
const plugins = [
|
|
121
137
|
openNextReplacementPlugin({
|
|
122
138
|
name: `requestHandlerOverride ${name}`,
|
|
@@ -124,6 +140,7 @@ async function generateBundle(name, options, fnOptions) {
|
|
|
124
140
|
deletes: [
|
|
125
141
|
...(disableNextPrebundledReact ? ["applyNextjsPrebundledReact"] : []),
|
|
126
142
|
...(disableRouting ? ["withRouting"] : []),
|
|
143
|
+
...(isAfter142 ? ["patchAsyncStorage"] : []),
|
|
127
144
|
],
|
|
128
145
|
}),
|
|
129
146
|
openNextReplacementPlugin({
|
|
@@ -141,6 +158,9 @@ async function generateBundle(name, options, fnOptions) {
|
|
|
141
158
|
fnName: name,
|
|
142
159
|
overrides,
|
|
143
160
|
}),
|
|
161
|
+
...additionalPlugins,
|
|
162
|
+
// The content updater plugin must be the last plugin
|
|
163
|
+
updater.plugin,
|
|
144
164
|
];
|
|
145
165
|
const outfileExt = fnOptions.runtime === "deno" ? "ts" : "mjs";
|
|
146
166
|
await buildHelper.esbuildAsync({
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { type Plugin } from "esbuild";
|
|
1
2
|
import type { MiddlewareInfo } from "../../types/next-types";
|
|
2
3
|
import type { IncludedConverter, IncludedOriginResolver, LazyLoadedOverride, OverrideOptions, SplittedFunctionOptions } from "../../types/open-next";
|
|
3
4
|
import type { OriginResolver } from "../../types/overrides.js";
|
|
5
|
+
import { ContentUpdater } from "../../plugins/content-updater.js";
|
|
4
6
|
import { type BuildOptions } from "../helper.js";
|
|
5
7
|
type Override = OverrideOptions & {
|
|
6
8
|
originResolver?: LazyLoadedOverride<OriginResolver> | IncludedOriginResolver;
|
|
@@ -17,7 +19,8 @@ interface BuildEdgeBundleOptions {
|
|
|
17
19
|
additionalExternals?: string[];
|
|
18
20
|
onlyBuildOnce?: boolean;
|
|
19
21
|
name: string;
|
|
22
|
+
additionalPlugins?: (contentUpdater: ContentUpdater) => Plugin[];
|
|
20
23
|
}
|
|
21
|
-
export declare function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, options, defaultConverter, overrides, additionalInject, includeCache, additionalExternals, onlyBuildOnce, name, }: BuildEdgeBundleOptions): Promise<void>;
|
|
22
|
-
export declare function generateEdgeBundle(name: string, options: BuildOptions, fnOptions: SplittedFunctionOptions): Promise<void>;
|
|
24
|
+
export declare function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, options, defaultConverter, overrides, additionalInject, includeCache, additionalExternals, onlyBuildOnce, name, additionalPlugins: additionalPluginsFn, }: BuildEdgeBundleOptions): Promise<void>;
|
|
25
|
+
export declare function generateEdgeBundle(name: string, options: BuildOptions, fnOptions: SplittedFunctionOptions, additionalPlugins?: (contentUpdater: ContentUpdater) => Plugin[]): Promise<void>;
|
|
23
26
|
export {};
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { build } from "esbuild";
|
|
5
5
|
import { loadMiddlewareManifest } from "../../adapters/config/util.js";
|
|
6
6
|
import logger from "../../logger.js";
|
|
7
|
+
import { ContentUpdater } from "../../plugins/content-updater.js";
|
|
7
8
|
import { openNextEdgePlugins } from "../../plugins/edge.js";
|
|
8
9
|
import { openNextExternalMiddlewarePlugin } from "../../plugins/externalMiddleware.js";
|
|
9
10
|
import { openNextReplacementPlugin } from "../../plugins/replacement.js";
|
|
@@ -11,13 +12,17 @@ import { openNextResolvePlugin } from "../../plugins/resolve.js";
|
|
|
11
12
|
import { getCrossPlatformPathRegex } from "../../utils/regex.js";
|
|
12
13
|
import { isEdgeRuntime } from "../helper.js";
|
|
13
14
|
import { copyOpenNextConfig, esbuildAsync } from "../helper.js";
|
|
14
|
-
export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, options, defaultConverter, overrides, additionalInject, includeCache, additionalExternals, onlyBuildOnce, name, }) {
|
|
15
|
+
export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, options, defaultConverter, overrides, additionalInject, includeCache, additionalExternals, onlyBuildOnce, name, additionalPlugins: additionalPluginsFn, }) {
|
|
15
16
|
const isInCloudfare = await isEdgeRuntime(overrides);
|
|
16
17
|
function override(target) {
|
|
17
18
|
return typeof overrides?.[target] === "string"
|
|
18
19
|
? overrides[target]
|
|
19
20
|
: undefined;
|
|
20
21
|
}
|
|
22
|
+
const contentUpdater = new ContentUpdater(options);
|
|
23
|
+
const additionalPlugins = additionalPluginsFn
|
|
24
|
+
? additionalPluginsFn(contentUpdater)
|
|
25
|
+
: [];
|
|
21
26
|
await esbuildAsync({
|
|
22
27
|
entryPoints: [entrypoint],
|
|
23
28
|
bundle: true,
|
|
@@ -53,6 +58,9 @@ export async function buildEdgeBundle({ middlewareInfo, entrypoint, outfile, opt
|
|
|
53
58
|
nextDir: path.join(options.appBuildOutputPath, ".next"),
|
|
54
59
|
isInCloudfare,
|
|
55
60
|
}),
|
|
61
|
+
...additionalPlugins,
|
|
62
|
+
// The content updater plugin must be the last plugin
|
|
63
|
+
contentUpdater.plugin,
|
|
56
64
|
],
|
|
57
65
|
treeShaking: true,
|
|
58
66
|
alias: {
|
|
@@ -117,7 +125,7 @@ Object.defineProperty = function(o, p, a) {
|
|
|
117
125
|
});
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
|
-
export async function generateEdgeBundle(name, options, fnOptions) {
|
|
128
|
+
export async function generateEdgeBundle(name, options, fnOptions, additionalPlugins = () => []) {
|
|
121
129
|
logger.info(`Generating edge bundle for: ${name}`);
|
|
122
130
|
const buildOutputDotNextDir = path.join(options.appBuildOutputPath, ".next");
|
|
123
131
|
// Create output folder
|
|
@@ -153,5 +161,6 @@ export async function generateEdgeBundle(name, options, fnOptions) {
|
|
|
153
161
|
overrides: fnOptions.override,
|
|
154
162
|
additionalExternals: options.config.edgeExternals,
|
|
155
163
|
name,
|
|
164
|
+
additionalPlugins,
|
|
156
165
|
});
|
|
157
166
|
}
|
package/dist/build/helper.d.ts
CHANGED
|
@@ -51,15 +51,16 @@ export declare function getHtmlPages(dotNextPath: string): Set<string>;
|
|
|
51
51
|
export declare function getBuildId(options: BuildOptions): string;
|
|
52
52
|
export declare function getOpenNextVersion(): string;
|
|
53
53
|
export declare function getNextVersion(appPath: string): string;
|
|
54
|
+
export type SemverOp = "=" | ">=" | "<=" | ">" | "<";
|
|
54
55
|
/**
|
|
55
56
|
* Compare two semver versions.
|
|
56
57
|
*
|
|
57
58
|
* @param v1 - First version. Can be "latest", otherwise it should be a valid semver version in the format of `major.minor.patch`. Usually is the next version from the package.json without canary suffix. If minor or patch are missing, they are considered 0.
|
|
58
59
|
* @param v2 - Second version. Should not be "latest", it should be a valid semver version in the format of `major.minor.patch`. If minor or patch are missing, they are considered 0.
|
|
59
60
|
* @example
|
|
60
|
-
*
|
|
61
|
+
* compareSemver("2.0.0", ">=", "1.0.0") === true
|
|
61
62
|
*/
|
|
62
|
-
export declare function compareSemver(v1: string, v2: string):
|
|
63
|
+
export declare function compareSemver(v1: string, operator: SemverOp, v2: string): boolean;
|
|
63
64
|
export declare function copyOpenNextConfig(inputDir: string, outputDir: string, isEdge?: boolean): void;
|
|
64
65
|
export declare function copyEnvFile(appPath: string, packagePath: string, outputPath: string): void;
|
|
65
66
|
/**
|
package/dist/build/helper.js
CHANGED
|
@@ -213,29 +213,54 @@ export function getNextVersion(appPath) {
|
|
|
213
213
|
* @param v1 - First version. Can be "latest", otherwise it should be a valid semver version in the format of `major.minor.patch`. Usually is the next version from the package.json without canary suffix. If minor or patch are missing, they are considered 0.
|
|
214
214
|
* @param v2 - Second version. Should not be "latest", it should be a valid semver version in the format of `major.minor.patch`. If minor or patch are missing, they are considered 0.
|
|
215
215
|
* @example
|
|
216
|
-
*
|
|
216
|
+
* compareSemver("2.0.0", ">=", "1.0.0") === true
|
|
217
217
|
*/
|
|
218
|
-
export function compareSemver(v1, v2) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if
|
|
222
|
-
|
|
223
|
-
|
|
218
|
+
export function compareSemver(v1, operator, v2) {
|
|
219
|
+
// - = 0 when versions are equal
|
|
220
|
+
// - > 0 if v1 > v2
|
|
221
|
+
// - < 0 if v2 > v1
|
|
222
|
+
let versionDiff = 0;
|
|
223
|
+
if (v1 === "latest") {
|
|
224
|
+
versionDiff = 1;
|
|
224
225
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
else {
|
|
227
|
+
if (/^[^\d]/.test(v1)) {
|
|
228
|
+
// biome-ignore lint/style/noParameterAssign:
|
|
229
|
+
v1 = v1.substring(1);
|
|
230
|
+
}
|
|
231
|
+
if (/^[^\d]/.test(v2)) {
|
|
232
|
+
// biome-ignore lint/style/noParameterAssign:
|
|
233
|
+
v2 = v2.substring(1);
|
|
234
|
+
}
|
|
235
|
+
const [major1, minor1 = 0, patch1 = 0] = v1.split(".").map(Number);
|
|
236
|
+
const [major2, minor2 = 0, patch2 = 0] = v2.split(".").map(Number);
|
|
237
|
+
if (Number.isNaN(major1) || Number.isNaN(major2)) {
|
|
238
|
+
throw new Error("The major version is required.");
|
|
239
|
+
}
|
|
240
|
+
if (major1 !== major2) {
|
|
241
|
+
versionDiff = major1 - major2;
|
|
242
|
+
}
|
|
243
|
+
else if (minor1 !== minor2) {
|
|
244
|
+
versionDiff = minor1 - minor2;
|
|
245
|
+
}
|
|
246
|
+
else if (patch1 !== patch2) {
|
|
247
|
+
versionDiff = patch1 - patch2;
|
|
248
|
+
}
|
|
228
249
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
250
|
+
switch (operator) {
|
|
251
|
+
case "=":
|
|
252
|
+
return versionDiff === 0;
|
|
253
|
+
case ">=":
|
|
254
|
+
return versionDiff >= 0;
|
|
255
|
+
case "<=":
|
|
256
|
+
return versionDiff <= 0;
|
|
257
|
+
case ">":
|
|
258
|
+
return versionDiff > 0;
|
|
259
|
+
case "<":
|
|
260
|
+
return versionDiff < 0;
|
|
261
|
+
default:
|
|
262
|
+
throw new Error(`Unsupported operator: ${operator}`);
|
|
233
263
|
}
|
|
234
|
-
if (major1 !== major2)
|
|
235
|
-
return major1 - major2;
|
|
236
|
-
if (minor1 !== minor2)
|
|
237
|
-
return minor1 - minor2;
|
|
238
|
-
return patch1 - patch2;
|
|
239
264
|
}
|
|
240
265
|
export function copyOpenNextConfig(inputDir, outputDir, isEdge = false) {
|
|
241
266
|
// Copy open-next.config.mjs
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Edit, Lang, type NapiConfig, type SgNode } from "@ast-grep/napi";
|
|
2
|
+
import type { PatchCodeFn } from "./codePatcher";
|
|
3
|
+
export type * from "@ast-grep/napi";
|
|
4
|
+
/**
|
|
5
|
+
* fix has the same meaning as in yaml rules
|
|
6
|
+
* see https://ast-grep.github.io/guide/rewrite-code.html#using-fix-in-yaml-rule
|
|
7
|
+
*/
|
|
8
|
+
export type RuleConfig = NapiConfig & {
|
|
9
|
+
fix?: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Returns the `Edit`s and `Match`es for an ast-grep rule in yaml format
|
|
13
|
+
*
|
|
14
|
+
* The rule must have a `fix` to rewrite the matched node.
|
|
15
|
+
*
|
|
16
|
+
* Tip: use https://ast-grep.github.io/playground.html to create rules.
|
|
17
|
+
*
|
|
18
|
+
* @param rule The rule. Either a yaml string or an instance of `RuleConfig`
|
|
19
|
+
* @param root The root node
|
|
20
|
+
* @param once only apply once
|
|
21
|
+
* @returns A list of edits and a list of matches.
|
|
22
|
+
*/
|
|
23
|
+
export declare function applyRule(rule: string | RuleConfig, root: SgNode, { once }?: {
|
|
24
|
+
once?: boolean | undefined;
|
|
25
|
+
}): {
|
|
26
|
+
edits: Edit[];
|
|
27
|
+
matches: SgNode[];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Parse a file and obtain its root.
|
|
31
|
+
*
|
|
32
|
+
* @param path The file path
|
|
33
|
+
* @param lang The language to parse. Defaults to TypeScript.
|
|
34
|
+
* @returns The root for the file.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseFile(path: string, lang?: Lang): SgNode;
|
|
37
|
+
/**
|
|
38
|
+
* Patches the code from by applying the rule.
|
|
39
|
+
*
|
|
40
|
+
* This function is mainly for on off edits and tests,
|
|
41
|
+
* use `getRuleEdits` to apply multiple rules.
|
|
42
|
+
*
|
|
43
|
+
* @param code The source code
|
|
44
|
+
* @param rule The astgrep rule (yaml or NapiConfig)
|
|
45
|
+
* @param lang The language used by the source code
|
|
46
|
+
* @param lang Whether to apply the rule only once
|
|
47
|
+
* @returns The patched code
|
|
48
|
+
*/
|
|
49
|
+
export declare function patchCode(code: string, rule: string | RuleConfig, { lang, once }?: {
|
|
50
|
+
lang?: Lang | undefined;
|
|
51
|
+
once?: boolean | undefined;
|
|
52
|
+
}): string;
|
|
53
|
+
/**
|
|
54
|
+
* @param rule
|
|
55
|
+
* @param lang
|
|
56
|
+
* @returns a callback applying the the rule.
|
|
57
|
+
*/
|
|
58
|
+
export declare function createPatchCode(rule: string | RuleConfig, lang?: Lang): PatchCodeFn;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Mostly copied from the cloudflare adapter
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { Lang, parse, } from "@ast-grep/napi";
|
|
4
|
+
import yaml from "yaml";
|
|
5
|
+
/**
|
|
6
|
+
* Returns the `Edit`s and `Match`es for an ast-grep rule in yaml format
|
|
7
|
+
*
|
|
8
|
+
* The rule must have a `fix` to rewrite the matched node.
|
|
9
|
+
*
|
|
10
|
+
* Tip: use https://ast-grep.github.io/playground.html to create rules.
|
|
11
|
+
*
|
|
12
|
+
* @param rule The rule. Either a yaml string or an instance of `RuleConfig`
|
|
13
|
+
* @param root The root node
|
|
14
|
+
* @param once only apply once
|
|
15
|
+
* @returns A list of edits and a list of matches.
|
|
16
|
+
*/
|
|
17
|
+
export function applyRule(rule, root, { once = false } = {}) {
|
|
18
|
+
const ruleConfig = typeof rule === "string" ? yaml.parse(rule) : rule;
|
|
19
|
+
if (ruleConfig.transform) {
|
|
20
|
+
throw new Error("transform is not supported");
|
|
21
|
+
}
|
|
22
|
+
if (!ruleConfig.fix) {
|
|
23
|
+
throw new Error("no fix to apply");
|
|
24
|
+
}
|
|
25
|
+
const fix = ruleConfig.fix;
|
|
26
|
+
const matches = once
|
|
27
|
+
? [root.find(ruleConfig)].filter((m) => m !== null)
|
|
28
|
+
: root.findAll(ruleConfig);
|
|
29
|
+
const edits = [];
|
|
30
|
+
matches.forEach((match) => {
|
|
31
|
+
edits.push(match.replace(
|
|
32
|
+
// Replace known placeholders by their value
|
|
33
|
+
fix
|
|
34
|
+
.replace(/\$\$\$([A-Z0-9_]+)/g, (_m, name) => match
|
|
35
|
+
.getMultipleMatches(name)
|
|
36
|
+
.map((n) => n.text())
|
|
37
|
+
.join(""))
|
|
38
|
+
.replace(/\$([A-Z0-9_]+)/g, (m, name) => match.getMatch(name)?.text() ?? m)));
|
|
39
|
+
});
|
|
40
|
+
return { edits, matches };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parse a file and obtain its root.
|
|
44
|
+
*
|
|
45
|
+
* @param path The file path
|
|
46
|
+
* @param lang The language to parse. Defaults to TypeScript.
|
|
47
|
+
* @returns The root for the file.
|
|
48
|
+
*/
|
|
49
|
+
export function parseFile(path, lang = Lang.TypeScript) {
|
|
50
|
+
return parse(lang, readFileSync(path, { encoding: "utf-8" })).root();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Patches the code from by applying the rule.
|
|
54
|
+
*
|
|
55
|
+
* This function is mainly for on off edits and tests,
|
|
56
|
+
* use `getRuleEdits` to apply multiple rules.
|
|
57
|
+
*
|
|
58
|
+
* @param code The source code
|
|
59
|
+
* @param rule The astgrep rule (yaml or NapiConfig)
|
|
60
|
+
* @param lang The language used by the source code
|
|
61
|
+
* @param lang Whether to apply the rule only once
|
|
62
|
+
* @returns The patched code
|
|
63
|
+
*/
|
|
64
|
+
export function patchCode(code, rule, { lang = Lang.TypeScript, once = false } = {}) {
|
|
65
|
+
const node = parse(lang, code).root();
|
|
66
|
+
const { edits } = applyRule(rule, node, { once });
|
|
67
|
+
return node.commitEdits(edits);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* @param rule
|
|
71
|
+
* @param lang
|
|
72
|
+
* @returns a callback applying the the rule.
|
|
73
|
+
*/
|
|
74
|
+
export function createPatchCode(rule, lang = Lang.TypeScript) {
|
|
75
|
+
return async ({ code }) => patchCode(code, rule, { lang });
|
|
76
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { getManifests } from "../copyTracedFiles.js";
|
|
2
|
+
import * as buildHelper from "../helper.js";
|
|
3
|
+
type Versions = `>=${number}.${number}.${number} <=${number}.${number}.${number}` | `>=${number}.${number}.${number}` | `<=${number}.${number}.${number}`;
|
|
4
|
+
export interface VersionedField<T> {
|
|
5
|
+
/**
|
|
6
|
+
* The versions of Next.js that this field should be used for
|
|
7
|
+
* Should be in the format `">=16.0.0 <=17.0.0"` or `">=16.0.0"` or `"<=17.0.0"`
|
|
8
|
+
* **Be careful with spaces**
|
|
9
|
+
*/
|
|
10
|
+
versions?: Versions;
|
|
11
|
+
field: T;
|
|
12
|
+
}
|
|
13
|
+
export type PatchCodeFn = (args: {
|
|
14
|
+
/**
|
|
15
|
+
* The code of the file that needs to be patched
|
|
16
|
+
*/
|
|
17
|
+
code: string;
|
|
18
|
+
/**
|
|
19
|
+
* The final path of the file that needs to be patched
|
|
20
|
+
*/
|
|
21
|
+
filePath: string;
|
|
22
|
+
/**
|
|
23
|
+
* All files that are traced and will be included in the bundle
|
|
24
|
+
*/
|
|
25
|
+
tracedFiles: string[];
|
|
26
|
+
/**
|
|
27
|
+
* Next.js manifests that are used by Next at runtime
|
|
28
|
+
*/
|
|
29
|
+
manifests: ReturnType<typeof getManifests>;
|
|
30
|
+
}) => Promise<string>;
|
|
31
|
+
interface IndividualPatch {
|
|
32
|
+
pathFilter: RegExp;
|
|
33
|
+
contentFilter?: RegExp;
|
|
34
|
+
patchCode: PatchCodeFn;
|
|
35
|
+
}
|
|
36
|
+
export interface CodePatcher {
|
|
37
|
+
name: string;
|
|
38
|
+
patches: IndividualPatch | VersionedField<IndividualPatch>[];
|
|
39
|
+
}
|
|
40
|
+
export declare function parseVersions(versions?: Versions): {
|
|
41
|
+
before?: string;
|
|
42
|
+
after?: string;
|
|
43
|
+
};
|
|
44
|
+
export declare function extractVersionedField<T>(fields: VersionedField<T>[], version: string): T[];
|
|
45
|
+
export declare function applyCodePatches(buildOptions: buildHelper.BuildOptions, tracedFiles: string[], manifests: ReturnType<typeof getManifests>, codePatcher: CodePatcher[]): Promise<void>;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import logger from "../../logger.js";
|
|
3
|
+
import * as buildHelper from "../helper.js";
|
|
4
|
+
export function parseVersions(versions) {
|
|
5
|
+
if (!versions) {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
8
|
+
// We need to use regex to extract the versions
|
|
9
|
+
const versionRegex = /([<>]=)(\d+\.\d+\.\d+)/g;
|
|
10
|
+
const matches = Array.from(versions.matchAll(versionRegex));
|
|
11
|
+
if (matches.length === 0) {
|
|
12
|
+
throw new Error("Invalid version range, no matches found");
|
|
13
|
+
}
|
|
14
|
+
if (matches.length > 2) {
|
|
15
|
+
throw new Error("Invalid version range, too many matches found");
|
|
16
|
+
}
|
|
17
|
+
let after;
|
|
18
|
+
let before;
|
|
19
|
+
for (const match of matches) {
|
|
20
|
+
const [_, operator, version] = match;
|
|
21
|
+
if (operator === "<=") {
|
|
22
|
+
before = version;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
after = version;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Before returning we reconstruct the version string and compare it to the original
|
|
29
|
+
// If they don't match we throw an error
|
|
30
|
+
// We have to do this because template literal types here seems to allow for extra spaces
|
|
31
|
+
// that could easily break the version comparison and allow some patch to be applied on incorrect versions
|
|
32
|
+
// This might even go unnoticed
|
|
33
|
+
const reconstructedVersion = `${after ? `>=${after}` : ""}${before ? `${after ? " " : ""}<=${before}` : ""}`;
|
|
34
|
+
if (reconstructedVersion !== versions) {
|
|
35
|
+
throw new Error("Invalid version range, the reconstructed version does not match the original");
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
before,
|
|
39
|
+
after,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function extractVersionedField(fields, version) {
|
|
43
|
+
const result = [];
|
|
44
|
+
for (const field of fields) {
|
|
45
|
+
// No versions specified, the patch always apply
|
|
46
|
+
if (!field.versions) {
|
|
47
|
+
result.push(field.field);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const { before, after } = parseVersions(field.versions);
|
|
51
|
+
// range
|
|
52
|
+
if (before && after) {
|
|
53
|
+
if (buildHelper.compareSemver(version, "<=", before) &&
|
|
54
|
+
buildHelper.compareSemver(version, ">=", after)) {
|
|
55
|
+
result.push(field.field);
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
// before only
|
|
60
|
+
if (before) {
|
|
61
|
+
if (buildHelper.compareSemver(version, "<=", before)) {
|
|
62
|
+
result.push(field.field);
|
|
63
|
+
}
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// after only
|
|
67
|
+
if (after) {
|
|
68
|
+
if (buildHelper.compareSemver(version, ">=", after)) {
|
|
69
|
+
result.push(field.field);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
export async function applyCodePatches(buildOptions, tracedFiles, manifests, codePatcher) {
|
|
76
|
+
const nextVersion = buildOptions.nextVersion;
|
|
77
|
+
logger.time("Applying code patches");
|
|
78
|
+
// We first filter against the version
|
|
79
|
+
// We also flatten the array of patches so that we get both the name and all the necessary patches
|
|
80
|
+
const patchesToApply = codePatcher.flatMap(({ name, patches }) => Array.isArray(patches)
|
|
81
|
+
? extractVersionedField(patches, nextVersion).map((patch) => ({
|
|
82
|
+
name,
|
|
83
|
+
patch,
|
|
84
|
+
}))
|
|
85
|
+
: [{ name, patch: patches }]);
|
|
86
|
+
await Promise.all(tracedFiles.map(async (filePath) => {
|
|
87
|
+
// We check the filename against the filter to see if we should apply the patch
|
|
88
|
+
const patchMatchingPath = patchesToApply.filter(({ patch }) => {
|
|
89
|
+
return filePath.match(patch.pathFilter);
|
|
90
|
+
});
|
|
91
|
+
if (patchMatchingPath.length === 0) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
95
|
+
// We filter a last time against the content this time
|
|
96
|
+
const patchToApply = patchMatchingPath.filter(({ patch }) => {
|
|
97
|
+
if (!patch.contentFilter) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return content.match(patch.contentFilter);
|
|
101
|
+
});
|
|
102
|
+
if (patchToApply.length === 0) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// We apply the patches
|
|
106
|
+
let patchedContent = content;
|
|
107
|
+
for (const { patch, name } of patchToApply) {
|
|
108
|
+
logger.debug(`Applying code patch: ${name} to ${filePath}`);
|
|
109
|
+
patchedContent = await patch.patchCode({
|
|
110
|
+
code: patchedContent,
|
|
111
|
+
filePath,
|
|
112
|
+
tracedFiles,
|
|
113
|
+
manifests,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
await fs.writeFile(filePath, patchedContent);
|
|
117
|
+
}));
|
|
118
|
+
logger.timeEnd("Applying code patches");
|
|
119
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CodePatcher } from "./codePatcher";
|
|
2
|
+
export declare const fetchRule = "\nrule:\n kind: member_expression\n pattern: $WORK_STORE.isOnDemandRevalidate\n inside:\n kind: ternary_expression\n all:\n - has: {kind: 'null'}\n - has: \n kind: await_expression\n has:\n kind: call_expression\n all:\n - has:\n kind: member_expression\n has:\n kind: property_identifier\n field: property\n regex: get\n - has:\n kind: arguments\n has:\n kind: object\n has:\n kind: pair\n all:\n - has:\n kind: property_identifier\n field: key\n regex: softTags\n inside:\n kind: variable_declarator\n\nfix:\n ($WORK_STORE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)\n";
|
|
3
|
+
export declare const unstable_cacheRule = "\nrule:\n kind: member_expression\n pattern: $STORE_OR_CACHE.isOnDemandRevalidate\n inside:\n kind: if_statement\n stopBy: end\n has:\n kind: statement_block\n has:\n kind: variable_declarator\n has: \n kind: await_expression\n has:\n kind: call_expression\n all:\n - has:\n kind: member_expression\n has:\n kind: property_identifier\n field: property\n regex: get\n - has:\n kind: arguments\n has:\n kind: object\n has:\n kind: pair\n all:\n - has:\n kind: property_identifier\n field: key\n regex: softTags\n stopBy: end\nfix:\n ($STORE_OR_CACHE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)\n";
|
|
4
|
+
export declare const patchFetchCacheForISR: CodePatcher;
|
|
5
|
+
export declare const patchUnstableCacheForISR: CodePatcher;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Lang } from "@ast-grep/napi";
|
|
2
|
+
import { getCrossPlatformPathRegex } from "../../utils/regex.js";
|
|
3
|
+
import { createPatchCode } from "./astCodePatcher.js";
|
|
4
|
+
export const fetchRule = `
|
|
5
|
+
rule:
|
|
6
|
+
kind: member_expression
|
|
7
|
+
pattern: $WORK_STORE.isOnDemandRevalidate
|
|
8
|
+
inside:
|
|
9
|
+
kind: ternary_expression
|
|
10
|
+
all:
|
|
11
|
+
- has: {kind: 'null'}
|
|
12
|
+
- has:
|
|
13
|
+
kind: await_expression
|
|
14
|
+
has:
|
|
15
|
+
kind: call_expression
|
|
16
|
+
all:
|
|
17
|
+
- has:
|
|
18
|
+
kind: member_expression
|
|
19
|
+
has:
|
|
20
|
+
kind: property_identifier
|
|
21
|
+
field: property
|
|
22
|
+
regex: get
|
|
23
|
+
- has:
|
|
24
|
+
kind: arguments
|
|
25
|
+
has:
|
|
26
|
+
kind: object
|
|
27
|
+
has:
|
|
28
|
+
kind: pair
|
|
29
|
+
all:
|
|
30
|
+
- has:
|
|
31
|
+
kind: property_identifier
|
|
32
|
+
field: key
|
|
33
|
+
regex: softTags
|
|
34
|
+
inside:
|
|
35
|
+
kind: variable_declarator
|
|
36
|
+
|
|
37
|
+
fix:
|
|
38
|
+
($WORK_STORE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)
|
|
39
|
+
`;
|
|
40
|
+
export const unstable_cacheRule = `
|
|
41
|
+
rule:
|
|
42
|
+
kind: member_expression
|
|
43
|
+
pattern: $STORE_OR_CACHE.isOnDemandRevalidate
|
|
44
|
+
inside:
|
|
45
|
+
kind: if_statement
|
|
46
|
+
stopBy: end
|
|
47
|
+
has:
|
|
48
|
+
kind: statement_block
|
|
49
|
+
has:
|
|
50
|
+
kind: variable_declarator
|
|
51
|
+
has:
|
|
52
|
+
kind: await_expression
|
|
53
|
+
has:
|
|
54
|
+
kind: call_expression
|
|
55
|
+
all:
|
|
56
|
+
- has:
|
|
57
|
+
kind: member_expression
|
|
58
|
+
has:
|
|
59
|
+
kind: property_identifier
|
|
60
|
+
field: property
|
|
61
|
+
regex: get
|
|
62
|
+
- has:
|
|
63
|
+
kind: arguments
|
|
64
|
+
has:
|
|
65
|
+
kind: object
|
|
66
|
+
has:
|
|
67
|
+
kind: pair
|
|
68
|
+
all:
|
|
69
|
+
- has:
|
|
70
|
+
kind: property_identifier
|
|
71
|
+
field: key
|
|
72
|
+
regex: softTags
|
|
73
|
+
stopBy: end
|
|
74
|
+
fix:
|
|
75
|
+
($STORE_OR_CACHE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)
|
|
76
|
+
`;
|
|
77
|
+
export const patchFetchCacheForISR = {
|
|
78
|
+
name: "patch-fetch-cache-for-isr",
|
|
79
|
+
patches: [
|
|
80
|
+
{
|
|
81
|
+
versions: ">=14.0.0",
|
|
82
|
+
field: {
|
|
83
|
+
pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|.*\.runtime\..*\.js|patch-fetch\.js)$`, { escape: false }),
|
|
84
|
+
contentFilter: /\.isOnDemandRevalidate/,
|
|
85
|
+
patchCode: createPatchCode(fetchRule, Lang.JavaScript),
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
export const patchUnstableCacheForISR = {
|
|
91
|
+
name: "patch-unstable-cache-for-isr",
|
|
92
|
+
patches: [
|
|
93
|
+
{
|
|
94
|
+
versions: ">=14.2.0",
|
|
95
|
+
field: {
|
|
96
|
+
pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|.*\.runtime\..*\.js|spec-extension/unstable-cache\.js)$`, { escape: false }),
|
|
97
|
+
contentFilter: /\.isOnDemandRevalidate/,
|
|
98
|
+
patchCode: createPatchCode(unstable_cacheRule, Lang.JavaScript),
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CodePatcher } from "./codePatcher";
|
|
2
|
+
export declare const rule = "\nrule:\n kind: call_expression\n pattern: $PROMISE\n all:\n - has: { pattern: $_.arrayBuffer().then, stopBy: end }\n - has: { pattern: \"Buffer.from\", stopBy: end }\n - any:\n - inside:\n kind: sequence_expression\n inside:\n kind: return_statement\n - inside:\n kind: expression_statement\n precedes:\n kind: return_statement\n - has: { pattern: $_.FETCH, stopBy: end }\n\nfix: |\n globalThis.__openNextAls?.getStore()?.pendingPromiseRunner.add($PROMISE)\n";
|
|
3
|
+
export declare const patchFetchCacheSetMissingWaitUntil: CodePatcher;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getCrossPlatformPathRegex } from "../../utils/regex.js";
|
|
2
|
+
import { createPatchCode } from "./astCodePatcher.js";
|
|
3
|
+
export const rule = `
|
|
4
|
+
rule:
|
|
5
|
+
kind: call_expression
|
|
6
|
+
pattern: $PROMISE
|
|
7
|
+
all:
|
|
8
|
+
- has: { pattern: $_.arrayBuffer().then, stopBy: end }
|
|
9
|
+
- has: { pattern: "Buffer.from", stopBy: end }
|
|
10
|
+
- any:
|
|
11
|
+
- inside:
|
|
12
|
+
kind: sequence_expression
|
|
13
|
+
inside:
|
|
14
|
+
kind: return_statement
|
|
15
|
+
- inside:
|
|
16
|
+
kind: expression_statement
|
|
17
|
+
precedes:
|
|
18
|
+
kind: return_statement
|
|
19
|
+
- has: { pattern: $_.FETCH, stopBy: end }
|
|
20
|
+
|
|
21
|
+
fix: |
|
|
22
|
+
globalThis.__openNextAls?.getStore()?.pendingPromiseRunner.add($PROMISE)
|
|
23
|
+
`;
|
|
24
|
+
export const patchFetchCacheSetMissingWaitUntil = {
|
|
25
|
+
name: "patch-fetch-cache-set-missing-wait-until",
|
|
26
|
+
patches: [
|
|
27
|
+
{
|
|
28
|
+
versions: ">=15.0.0",
|
|
29
|
+
field: {
|
|
30
|
+
pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|.*\.runtime\..*\.js|patch-fetch\.js)$`, { escape: false }),
|
|
31
|
+
contentFilter: /arrayBuffer\(\)\s*\.then/,
|
|
32
|
+
patchCode: createPatchCode(rule),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
@@ -9,7 +9,9 @@ import routingHandler, { INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_R
|
|
|
9
9
|
import { requestHandler, setNextjsPrebundledReact } from "./util";
|
|
10
10
|
// This is used to identify requests in the cache
|
|
11
11
|
globalThis.__openNextAls = new AsyncLocalStorage();
|
|
12
|
+
//#override patchAsyncStorage
|
|
12
13
|
patchAsyncStorage();
|
|
14
|
+
//#endOverride
|
|
13
15
|
export async function openNextHandler(internalEvent, options) {
|
|
14
16
|
const initialHeaders = internalEvent.headers;
|
|
15
17
|
// We run everything in the async local storage context so that it is available in the middleware as well as in NextServer
|
|
@@ -94,7 +96,7 @@ export async function openNextHandler(internalEvent, options) {
|
|
|
94
96
|
resolvedRoutes: [],
|
|
95
97
|
origin: false,
|
|
96
98
|
initialURL: internalEvent.url,
|
|
97
|
-
}, headers, options.streamCreator);
|
|
99
|
+
}, routingResult.headers, options.streamCreator);
|
|
98
100
|
response.statusCode = routingResult.statusCode;
|
|
99
101
|
response.flushHeaders();
|
|
100
102
|
const [bodyToConsume, bodyToReturn] = routingResult.body.tee();
|
|
@@ -48,7 +48,12 @@ async function computeCacheControl(path, body, host, revalidate, lastModified) {
|
|
|
48
48
|
url = `${NextConfig.basePath}${url}`;
|
|
49
49
|
}
|
|
50
50
|
await globalThis.queue.send({
|
|
51
|
-
MessageBody: {
|
|
51
|
+
MessageBody: {
|
|
52
|
+
host,
|
|
53
|
+
url,
|
|
54
|
+
eTag: etag,
|
|
55
|
+
lastModified: lastModified ?? Date.now(),
|
|
56
|
+
},
|
|
52
57
|
MessageDeduplicationId: hash(`${path}-${lastModified}-${etag}`),
|
|
53
58
|
MessageGroupId: generateMessageGroupId(path),
|
|
54
59
|
});
|
|
@@ -3,12 +3,12 @@ import type { InternalEvent, InternalResult } from "../../types/open-next";
|
|
|
3
3
|
export declare function getNextConfigHeaders(event: InternalEvent, configHeaders?: Header[] | undefined): Record<string, string | undefined>;
|
|
4
4
|
export declare function handleRewrites<T extends RewriteDefinition>(event: InternalEvent, rewrites: T[]): {
|
|
5
5
|
internalEvent: {
|
|
6
|
+
query: Record<string, string | string[]>;
|
|
6
7
|
rawPath: string;
|
|
7
8
|
url: string;
|
|
8
9
|
method: string;
|
|
9
10
|
body?: Buffer;
|
|
10
11
|
headers: Record<string, string>;
|
|
11
|
-
query: Record<string, string | string[]>;
|
|
12
12
|
cookies: Record<string, string>;
|
|
13
13
|
remoteAddress: string;
|
|
14
14
|
type: "core";
|
|
@@ -145,10 +145,17 @@ export function handleRewrites(event, rewrites) {
|
|
|
145
145
|
rewrittenHost = unescapeRegex(toDestinationHost(params));
|
|
146
146
|
rewrittenQuery = unescapeRegex(toDestinationQuery(params));
|
|
147
147
|
}
|
|
148
|
+
// We need to strip the locale from the path if it's a local api route
|
|
149
|
+
if (NextConfig.i18n && !isExternalRewrite) {
|
|
150
|
+
const strippedPathLocale = rewrittenPath.replace(new RegExp(`^/(${NextConfig.i18n.locales.join("|")})`), "");
|
|
151
|
+
if (strippedPathLocale.startsWith("/api/")) {
|
|
152
|
+
rewrittenPath = strippedPathLocale;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
148
155
|
rewrittenUrl = isExternalRewrite
|
|
149
156
|
? `${protocol}//${rewrittenHost}${rewrittenPath}`
|
|
150
157
|
: new URL(rewrittenPath, event.url).href;
|
|
151
|
-
//
|
|
158
|
+
// We merge query params from the source and the destination
|
|
152
159
|
finalQuery = {
|
|
153
160
|
...query,
|
|
154
161
|
...convertFromQueryString(rewrittenQuery),
|
|
@@ -159,6 +166,7 @@ export function handleRewrites(event, rewrites) {
|
|
|
159
166
|
return {
|
|
160
167
|
internalEvent: {
|
|
161
168
|
...event,
|
|
169
|
+
query: finalQuery,
|
|
162
170
|
rawPath: new URL(rewrittenUrl).pathname,
|
|
163
171
|
url: rewrittenUrl,
|
|
164
172
|
},
|
|
@@ -73,6 +73,12 @@ export async function handleMiddleware(internalEvent, middlewareLoader = default
|
|
|
73
73
|
const reqHeaders = {};
|
|
74
74
|
const resHeaders = {};
|
|
75
75
|
responseHeaders.delete("x-middleware-override-headers");
|
|
76
|
+
/* Next will set the header `x-middleware-set-cookie` when you `set-cookie` in the middleware.
|
|
77
|
+
* We can delete it here since it will be set in `set-cookie` aswell. Next removes this header in the response themselves.
|
|
78
|
+
* `x-middleware-next` is set when you invoke `NextResponse.next()`. We can delete it here aswell.
|
|
79
|
+
*/
|
|
80
|
+
responseHeaders.delete("x-middleware-set-cookie");
|
|
81
|
+
responseHeaders.delete("x-middleware-next");
|
|
76
82
|
const xMiddlewareKey = "x-middleware-request-";
|
|
77
83
|
responseHeaders.forEach((value, key) => {
|
|
78
84
|
if (key.startsWith(xMiddlewareKey)) {
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
export function generateShardId(rawPath, maxConcurrency, prefix) {
|
|
2
|
+
let a = cyrb128(rawPath);
|
|
3
|
+
// We use mulberry32 to generate a random int between 0 and MAX_REVALIDATE_CONCURRENCY
|
|
4
|
+
let t = (a += 0x6d2b79f5);
|
|
5
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
6
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
7
|
+
const randomFloat = ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
8
|
+
// This will generate a random int between 0 and maxConcurrency
|
|
9
|
+
const randomInt = Math.floor(randomFloat * maxConcurrency);
|
|
10
|
+
return `${prefix}-${randomInt}`;
|
|
11
|
+
}
|
|
1
12
|
// Since we're using a FIFO queue, every messageGroupId is treated sequentially
|
|
2
13
|
// This could cause a backlog of messages in the queue if there is too much page to
|
|
3
14
|
// revalidate at once. To avoid this, we generate a random messageGroupId for each
|
|
@@ -6,17 +17,10 @@
|
|
|
6
17
|
// will always have the same messageGroupId.
|
|
7
18
|
// https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript#answer-47593316
|
|
8
19
|
export function generateMessageGroupId(rawPath) {
|
|
9
|
-
let a = cyrb128(rawPath);
|
|
10
|
-
// We use mulberry32 to generate a random int between 0 and MAX_REVALIDATE_CONCURRENCY
|
|
11
|
-
let t = (a += 0x6d2b79f5);
|
|
12
|
-
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
13
|
-
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
14
|
-
const randomFloat = ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
15
20
|
// This will generate a random int between 0 and MAX_REVALIDATE_CONCURRENCY
|
|
16
21
|
// This means that we could have 1000 revalidate request at the same time
|
|
17
22
|
const maxConcurrency = Number.parseInt(process.env.MAX_REVALIDATE_CONCURRENCY ?? "10");
|
|
18
|
-
|
|
19
|
-
return `revalidate-${randomInt}`;
|
|
23
|
+
return generateShardId(rawPath, maxConcurrency, "revalidate");
|
|
20
24
|
}
|
|
21
25
|
// Used to generate a hash int from a string
|
|
22
26
|
function cyrb128(str) {
|
|
@@ -268,10 +268,10 @@ export async function revalidateIfRequired(host, rawPath, headers, req) {
|
|
|
268
268
|
const lastModified = globalThis.__openNextAls.getStore()?.lastModified ?? 0;
|
|
269
269
|
// For some weird cases, lastModified is not set, haven't been able to figure out yet why
|
|
270
270
|
// For those cases we add the etag to the deduplication id, it might help
|
|
271
|
-
const
|
|
271
|
+
const eTag = `${headers.etag ?? headers.ETag ?? ""}`;
|
|
272
272
|
await globalThis.queue.send({
|
|
273
|
-
MessageBody: { host, url: revalidateUrl },
|
|
274
|
-
MessageDeduplicationId: hash(`${rawPath}-${lastModified}-${
|
|
273
|
+
MessageBody: { host, url: revalidateUrl, eTag, lastModified },
|
|
274
|
+
MessageDeduplicationId: hash(`${rawPath}-${lastModified}-${eTag}`),
|
|
275
275
|
MessageGroupId: generateMessageGroupId(rawPath),
|
|
276
276
|
});
|
|
277
277
|
}
|
package/dist/logger.d.ts
CHANGED
|
@@ -8,5 +8,13 @@ declare const _default: {
|
|
|
8
8
|
};
|
|
9
9
|
warn: (...args: any[]) => void;
|
|
10
10
|
error: (...args: any[]) => void;
|
|
11
|
+
time: {
|
|
12
|
+
(label?: string): void;
|
|
13
|
+
(label?: string): void;
|
|
14
|
+
};
|
|
15
|
+
timeEnd: {
|
|
16
|
+
(label?: string): void;
|
|
17
|
+
(label?: string): void;
|
|
18
|
+
};
|
|
11
19
|
};
|
|
12
20
|
export default _default;
|
package/dist/logger.js
CHANGED
|
@@ -19,7 +19,9 @@ const wrapper = async (handler, converter) => {
|
|
|
19
19
|
const internalEvent = await converter.convertFrom(req);
|
|
20
20
|
const streamCreator = {
|
|
21
21
|
writeHeaders: (prelude) => {
|
|
22
|
+
res.setHeader("Set-Cookie", prelude.cookies);
|
|
22
23
|
res.writeHead(prelude.statusCode, prelude.headers);
|
|
24
|
+
res.flushHeaders();
|
|
23
25
|
return res;
|
|
24
26
|
},
|
|
25
27
|
onFinish: () => { },
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESBuild stops calling `onLoad` hooks after the first hook returns an updated content.
|
|
3
|
+
*
|
|
4
|
+
* The updater allows multiple plugins to update the content.
|
|
5
|
+
*/
|
|
6
|
+
import type { OnLoadOptions, Plugin, PluginBuild } from "esbuild";
|
|
7
|
+
import type { BuildOptions } from "../build/helper";
|
|
8
|
+
import { type VersionedField } from "../build/patch/codePatcher.js";
|
|
9
|
+
export type * from "esbuild";
|
|
10
|
+
/**
|
|
11
|
+
* The callbacks returns either an updated content or undefined if the content is unchanged.
|
|
12
|
+
*/
|
|
13
|
+
export type Callback = (args: {
|
|
14
|
+
contents: string;
|
|
15
|
+
path: string;
|
|
16
|
+
}) => string | undefined | Promise<string | undefined>;
|
|
17
|
+
/**
|
|
18
|
+
* The callback is called only when `contentFilter` matches the content.
|
|
19
|
+
* It can be used as a fast heuristic to prevent an expensive update.
|
|
20
|
+
*/
|
|
21
|
+
export type OnUpdateOptions = OnLoadOptions & {
|
|
22
|
+
contentFilter: RegExp;
|
|
23
|
+
};
|
|
24
|
+
export type Updater = OnUpdateOptions & {
|
|
25
|
+
callback: Callback;
|
|
26
|
+
};
|
|
27
|
+
export declare class ContentUpdater {
|
|
28
|
+
private buildOptions;
|
|
29
|
+
updaters: Map<string, Updater[]>;
|
|
30
|
+
constructor(buildOptions: BuildOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Register a callback to update the file content.
|
|
33
|
+
*
|
|
34
|
+
* The callbacks are called in order of registration.
|
|
35
|
+
*
|
|
36
|
+
* @param name The name of the plugin (must be unique).
|
|
37
|
+
* @param updater A versioned field with the callback and `OnUpdateOptions`.
|
|
38
|
+
* @returns A noop ESBuild plugin.
|
|
39
|
+
*/
|
|
40
|
+
updateContent(name: string, versionedUpdaters: VersionedField<Updater>[]): Plugin;
|
|
41
|
+
/**
|
|
42
|
+
* Returns an ESBuild plugin applying the registered updates.
|
|
43
|
+
*/
|
|
44
|
+
get plugin(): {
|
|
45
|
+
name: string;
|
|
46
|
+
setup: (build: PluginBuild) => Promise<void>;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESBuild stops calling `onLoad` hooks after the first hook returns an updated content.
|
|
3
|
+
*
|
|
4
|
+
* The updater allows multiple plugins to update the content.
|
|
5
|
+
*/
|
|
6
|
+
import { readFile } from "node:fs/promises";
|
|
7
|
+
import { extractVersionedField, } from "../build/patch/codePatcher.js";
|
|
8
|
+
export class ContentUpdater {
|
|
9
|
+
buildOptions;
|
|
10
|
+
updaters = new Map();
|
|
11
|
+
constructor(buildOptions) {
|
|
12
|
+
this.buildOptions = buildOptions;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Register a callback to update the file content.
|
|
16
|
+
*
|
|
17
|
+
* The callbacks are called in order of registration.
|
|
18
|
+
*
|
|
19
|
+
* @param name The name of the plugin (must be unique).
|
|
20
|
+
* @param updater A versioned field with the callback and `OnUpdateOptions`.
|
|
21
|
+
* @returns A noop ESBuild plugin.
|
|
22
|
+
*/
|
|
23
|
+
updateContent(name, versionedUpdaters) {
|
|
24
|
+
if (this.updaters.has(name)) {
|
|
25
|
+
throw new Error(`Plugin "${name}" already registered`);
|
|
26
|
+
}
|
|
27
|
+
const updaters = extractVersionedField(versionedUpdaters, this.buildOptions.nextVersion);
|
|
28
|
+
this.updaters.set(name, updaters);
|
|
29
|
+
return {
|
|
30
|
+
name,
|
|
31
|
+
setup() { },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns an ESBuild plugin applying the registered updates.
|
|
36
|
+
*/
|
|
37
|
+
get plugin() {
|
|
38
|
+
return {
|
|
39
|
+
name: "aggregate-on-load",
|
|
40
|
+
setup: async (build) => {
|
|
41
|
+
build.onLoad({ filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/ }, async (args) => {
|
|
42
|
+
const updaters = Array.from(this.updaters.values()).flat();
|
|
43
|
+
if (updaters.length === 0) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let contents = await readFile(args.path, "utf-8");
|
|
47
|
+
for (const { filter, namespace, contentFilter, callback, } of updaters) {
|
|
48
|
+
if (namespace !== undefined && args.namespace !== namespace) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (!args.path.match(filter)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (!contents.match(contentFilter)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
contents =
|
|
58
|
+
(await callback({ contents, path: args.path })) ?? contents;
|
|
59
|
+
}
|
|
60
|
+
return { contents };
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"access": "public"
|
|
4
4
|
},
|
|
5
5
|
"name": "@opennextjs/aws",
|
|
6
|
-
"version": "3.5.
|
|
6
|
+
"version": "3.5.3",
|
|
7
7
|
"bin": {
|
|
8
8
|
"open-next": "./dist/index.js"
|
|
9
9
|
},
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"README.md"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
+
"@ast-grep/napi": "^0.35.0",
|
|
36
37
|
"@aws-sdk/client-cloudfront": "3.398.0",
|
|
37
38
|
"@aws-sdk/client-dynamodb": "^3.398.0",
|
|
38
39
|
"@aws-sdk/client-lambda": "^3.398.0",
|
|
@@ -46,7 +47,8 @@
|
|
|
46
47
|
"esbuild": "0.19.2",
|
|
47
48
|
"express": "5.0.1",
|
|
48
49
|
"path-to-regexp": "^6.3.0",
|
|
49
|
-
"urlpattern-polyfill": "^10.0.0"
|
|
50
|
+
"urlpattern-polyfill": "^10.0.0",
|
|
51
|
+
"yaml": "^2.7.0"
|
|
50
52
|
},
|
|
51
53
|
"devDependencies": {
|
|
52
54
|
"@types/aws-lambda": "^8.10.109",
|