@aziontech/opennextjs-azion
Version:
Azion builder for Next.js apps
105 lines (104 loc) • 4.77 kB
JavaScript
/**
* This code was originally copied and modified from the @opennextjs/cloudflare repository.
* Significant changes have been made to adapt it for use with Azion.
*/
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import path from "node:path";
import logger from "@opennextjs/aws/logger.js";
import { globSync } from "glob";
import { tqdm } from "ts-tqdm";
import { CACHE_DIR as STATIC_ASSETS_CACHE_DIR, NAME as STATIC_ASSETS_CACHE_NAME, } from "../../core/overrides/incremental-cache/storage-incremental-cache.js";
import { computeCacheKey, DEFAULT_PREFIX, NAME_FILE_TAG_MANIFEST } from "../../core/overrides/internal.js";
async function resolveCacheName(value) {
return typeof value === "function" ? (await value()).name : value;
}
export function getCacheAssets(opts) {
const allFiles = globSync(path.join(opts.outputDir, "cache/**/*"), {
withFileTypes: true,
windowsPathsNoEscape: true,
}).filter((f) => f.isFile());
const assets = [];
for (const file of allFiles) {
const fullPath = file.fullpathPosix();
const relativePath = path.relative(path.join(opts.outputDir, "cache"), fullPath);
if (relativePath.startsWith("__fetch")) {
const [__fetch, buildId, ...keyParts] = relativePath.split("/");
if (__fetch !== "__fetch" || buildId === undefined || keyParts.length === 0) {
throw new Error(`Invalid path for a Cache Asset file: ${relativePath}`);
}
assets.push({
isFetch: true,
fullPath,
key: `/${keyParts.join("/")}`,
buildId,
});
}
else {
const [buildId, ...keyParts] = relativePath.slice(0, -".cache".length).split("/");
if (!relativePath.endsWith(".cache") || buildId === undefined || keyParts.length === 0) {
throw new Error(`Invalid path for a Cache Asset file: ${relativePath}`);
}
assets.push({
isFetch: false,
fullPath,
key: `/${keyParts.join("/")}`,
buildId,
});
}
}
return assets;
}
function populateStaticAssetsIncrementalCache(options, cacheDir) {
logger.info("Populating cache...");
const storageCacheDir = path.join(cacheDir, STATIC_ASSETS_CACHE_DIR);
if (existsSync(storageCacheDir)) {
rmSync(storageCacheDir, { recursive: true, force: true });
}
// Create the cache directory if it doesn't exist
mkdirSync(storageCacheDir, { recursive: true });
const assets = getCacheAssets(options);
logger.info(`Writing ${assets.length} cache files to ${storageCacheDir}`);
for (const asset of tqdm(assets)) {
const { fullPath, key, buildId, isFetch } = asset;
// Compute the cache key (this will be hashed for safe file names)
const cacheKey = computeCacheKey(key, {
prefix: DEFAULT_PREFIX,
buildId,
cacheType: isFetch ? "fetch" : "cache",
});
// Create the full file path in the cache directory
const cacheFilePath = path.join(storageCacheDir, cacheKey);
// Ensure the directory exists for nested paths
const cacheFileDir = path.dirname(cacheFilePath);
if (!existsSync(cacheFileDir)) {
mkdirSync(cacheFileDir, { recursive: true });
}
// Read the original file content and write it to the cache location
const fileContent = readFileSync(fullPath, "utf8");
writeFileSync(cacheFilePath, fileContent, "utf8");
}
// Copy tag manifest
if (existsSync(path.join(options.outputDir, "azion", NAME_FILE_TAG_MANIFEST))) {
const tagManifestPath = path.join(options.outputDir, "azion", NAME_FILE_TAG_MANIFEST);
cpSync(tagManifestPath, path.join(storageCacheDir, NAME_FILE_TAG_MANIFEST));
rmSync(tagManifestPath);
}
logger.info(`Successfully populated cache with ${assets.length} files`);
}
export async function populateCache(options, config, populateCacheOptions) {
const { incrementalCache } = config.default.override ?? {};
if (!existsSync(options.outputDir)) {
logger.error("Unable to populate cache: Open Next build not found");
process.exit(1);
}
if (!config.dangerous?.disableIncrementalCache && incrementalCache) {
const name = await resolveCacheName(incrementalCache);
switch (name) {
case STATIC_ASSETS_CACHE_NAME:
populateStaticAssetsIncrementalCache(options, populateCacheOptions.cacheDir);
break;
default:
logger.info("Incremental cache does not need populating");
}
}
}