Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .changeset/cache-handler-function.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
"@opennextjs/core": minor
---

Add a dedicated cache handler function

The incremental cache, the tag cache and CDN invalidation can now run in their own function, bundled
to `.open-next/cache-function` and reachable over the new `cache` override.

The overrides for that function are configured through a new top level `cacheHandler` option:

```ts
export default {
default: {
override: {
cache: "local",
},
},
cacheHandler: {
incrementalCache: "s3",
tagCache: "dynamodb",
},
} satisfies OpenNextConfig;
```

Three `cache` implementations ship with core: `fetch` (HTTP), `local` (in-process, imports the cache
bundle) and `dummy`. Adapters that do not want the bundle can opt out with `skipCache` in
`buildAdapter`.

This is additive: the existing `default.override.incrementalCache` and `default.override.tagCache`
keep working unchanged.
183 changes: 41 additions & 142 deletions packages/aws/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,147 +1,46 @@
import fs from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";

import { compileCache } from "@opennextjs/core/build/compileCache.js";
import { compileOpenNextConfig } from "@opennextjs/core/build/compileConfig.js";
import { compileTagCacheProvider } from "@opennextjs/core/build/compileTagCacheProvider.js";
import { createCacheAssets, createStaticAssets } from "@opennextjs/core/build/createAssets.js";
import { createImageOptimizationBundle } from "@opennextjs/core/build/createImageOptimizationBundle.js";
import { createMiddleware } from "@opennextjs/core/build/createMiddleware.js";
import { createRevalidationBundle } from "@opennextjs/core/build/createRevalidationBundle.js";
import { createServerBundle } from "@opennextjs/core/build/createServerBundle.js";
import { createWarmerBundle } from "@opennextjs/core/build/createWarmerBundle.js";
import { generateOutput } from "@opennextjs/core/build/generateOutput.js";
import { buildAdapter } from "@opennextjs/core/build/adapter.js";
import type { BuildOptions } from "@opennextjs/core/build/helper.js";
import * as buildHelper from "@opennextjs/core/build/helper.js";
import { addDebugFile } from "@opennextjs/core/debug.js";
import type { ContentUpdater } from "@opennextjs/core/plugins/content-updater.js";
import { externalChunksPlugin, inlineRouteHandler } from "@opennextjs/core/plugins/inlineRouteHandlers.js";
import type { NextConfig } from "@opennextjs/core/types/next-types.js";

export type NextAdapterOutput = {
pathname: string;
filePath: string;
assets: Record<string, unknown>;
};

export type NextAdapterOutputs = {
pages: NextAdapterOutput[];
pagesApi: NextAdapterOutput[];
appPages: NextAdapterOutput[];
appRoutes: NextAdapterOutput[];
middleware?: NextAdapterOutput;
};

type NextAdapter = {
name: string;
modifyConfig: (config: NextConfig, { phase }: { phase: string }) => Promise<NextConfig>;
onBuildComplete: (props: {
routes: unknown;
outputs: NextAdapterOutputs;
projectDir: string;
repoRoot: string;
distDir: string;
config: NextConfig;
nextVersion: string;
}) => Promise<void>;
}; //TODO: use the one provided by Next

let buildOpts: buildHelper.BuildOptions;

export default {
name: "OpenNext",
async modifyConfig(nextConfig, { phase }) {
// We have to precompile the cache here, probably compile OpenNext config as well
const { config, buildDir } = await compileOpenNextConfig("open-next.config.ts", {
nodeExternals: undefined,
});

const require = createRequire(import.meta.url);
//TODO: change that
const openNextDistDir = path.dirname(require.resolve("@opennextjs/core/debug.js"));

buildOpts = buildHelper.normalizeOptions(config, openNextDistDir, buildDir);

buildHelper.initOutputDir(buildOpts);

const cache = compileCache(buildOpts);

const packagePath = buildHelper.getPackagePath(buildOpts);

// We then have to copy the cache files to the .next dir so that they are available at runtime
//TODO: use a better path, this one is temporary just to make it work
const tempCachePath = path.join(
buildOpts.outputDir,
"server-functions/default",
packagePath,
".open-next/.build"
);
fs.mkdirSync(tempCachePath, { recursive: true });
fs.copyFileSync(cache.cache, path.join(tempCachePath, "cache.cjs"));
fs.copyFileSync(cache.composableCache, path.join(tempCachePath, "composable-cache.cjs"));

//TODO: We should check the version of Next here, below 16 we'd throw or show a warning
return {
...nextConfig,
cacheHandler: cache.cache, //TODO: compute that here,
cacheHandlers: {
default: cache.composableCache,
remote: cache.composableCache,
},
cacheMaxMemorySize: 0,
experimental: {
...nextConfig.experimental,
trustHostHeader: true,
},
};
import type { NextAdapterOutputs } from "@opennextjs/core/types/adapter.js";

export default buildAdapter((_config, buildOpts: BuildOptions) => ({
defaultOverrides: {
server: {
wrapper: "@opennextjs/aws/overrides/wrappers/aws-lambda-streaming.js",
converter: "@opennextjs/aws/overrides/converters/aws-apigw-v2.js",
incrementalCache: "@opennextjs/aws/overrides/incrementalCache/s3.js",
tagCache: "@opennextjs/aws/overrides/tagCache/dynamodb.js",
queue: "@opennextjs/aws/overrides/queue/sqs.js",
},
revalidation: {
wrapper: "@opennextjs/aws/overrides/wrappers/aws-lambda.js",
converter: "@opennextjs/aws/overrides/converters/sqs-revalidate.js",
},
imageOptimization: {
wrapper: "@opennextjs/aws/overrides/wrappers/aws-lambda.js",
converter: "@opennextjs/aws/overrides/converters/aws-apigw-v2.js",
imageLoader: "@opennextjs/aws/overrides/imageLoader/s3.js",
},
warmer: { wrapper: "@opennextjs/aws/overrides/wrappers/aws-lambda.js" },
tagCache: {
wrapper: "@opennextjs/aws/overrides/wrappers/aws-lambda.js",
tagCache: "@opennextjs/aws/overrides/tagCache/dynamodb.js",
},
middleware: {
wrapper: "@opennextjs/aws/overrides/wrappers/aws-lambda.js",
converter: "@opennextjs/aws/overrides/converters/aws-cloudfront.js",
incrementalCache: "@opennextjs/aws/overrides/incrementalCache/s3-lite.js",
tagCache: "@opennextjs/aws/overrides/tagCache/dynamodb-lite.js",
queue: "@opennextjs/aws/overrides/queue/sqs-lite.js",
},
},
async onBuildComplete(outputs) {
console.log("OpenNext build will start now");

// TODO(vicb): save outputs
addDebugFile(buildOpts, "outputs.json", outputs);

// Compile middleware
await createMiddleware(buildOpts);
console.log("Middleware created");

createStaticAssets(buildOpts);
console.log("Static assets created");

if (buildOpts.config.dangerous?.disableIncrementalCache !== true) {
const { useTagCache } = createCacheAssets(buildOpts);
console.log("Cache assets created");
if (useTagCache) {
await compileTagCacheProvider(buildOpts);
console.log("Tag cache provider compiled");
}
}

await createServerBundle(
buildOpts,
{
additionalPlugins: getAdditionalPluginsFactory(buildOpts, outputs.outputs),
},
outputs.outputs
);

console.log("Server bundle created");
await createRevalidationBundle(buildOpts);
console.log("Revalidation bundle created");
await createImageOptimizationBundle(buildOpts);
console.log("Image optimization bundle created");
await createWarmerBundle(buildOpts);
console.log("Warmer bundle created");
await generateOutput(buildOpts);
console.log("Output generated");
serverBundle: {
externals: ["./middleware.mjs"],
additionalPlugins: (updater: ContentUpdater, outputs: NextAdapterOutputs) => {
const packagePath = buildHelper.getPackagePath(buildOpts);
return [inlineRouteHandler(updater, outputs, packagePath), externalChunksPlugin(outputs, packagePath)];
},
},
} satisfies NextAdapter;

function getAdditionalPluginsFactory(buildOpts: buildHelper.BuildOptions, outputs: NextAdapterOutputs) {
//TODO: we should make this a property of buildOpts
const packagePath = buildHelper.getPackagePath(buildOpts);
return (updater: ContentUpdater) => [
inlineRouteHandler(updater, outputs, packagePath),
externalChunksPlugin(outputs, packagePath),
];
}
}));
10 changes: 0 additions & 10 deletions packages/aws/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,8 @@ import path from "node:path";
import url from "node:url";

import { buildNextjsApp, setStandaloneBuildMode } from "@opennextjs/core/build/buildNextApp.js";
import { compileCache } from "@opennextjs/core/build/compileCache.js";
import { compileOpenNextConfig } from "@opennextjs/core/build/compileConfig.js";
import { compileTagCacheProvider } from "@opennextjs/core/build/compileTagCacheProvider.js";
import { createCacheAssets, createStaticAssets } from "@opennextjs/core/build/createAssets.js";
import { createImageOptimizationBundle } from "@opennextjs/core/build/createImageOptimizationBundle.js";
import { createMiddleware } from "@opennextjs/core/build/createMiddleware.js";
import { createRevalidationBundle } from "@opennextjs/core/build/createRevalidationBundle.js";
import { createServerBundle } from "@opennextjs/core/build/createServerBundle.js";
import { createWarmerBundle } from "@opennextjs/core/build/createWarmerBundle.js";
import { generateOutput } from "@opennextjs/core/build/generateOutput.js";
import * as buildHelper from "@opennextjs/core/build/helper.js";
import { patchOriginalNextConfig } from "@opennextjs/core/build/patch/patches/index.js";
import { printHeader, showWarningOnWindows } from "@opennextjs/core/build/utils.js";
import logger from "@opennextjs/core/logger.js";

Expand Down
Loading
Loading