diff --git a/.changeset/popular-tomatoes-jog.md b/.changeset/popular-tomatoes-jog.md new file mode 100644 index 000000000..dc756a86d --- /dev/null +++ b/.changeset/popular-tomatoes-jog.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/aws": patch +--- + +fix: createAssets fails in case of broken JSON in fetch cache diff --git a/packages/open-next/src/build/createAssets.ts b/packages/open-next/src/build/createAssets.ts index 0fc224e68..027b8f8a7 100644 --- a/packages/open-next/src/build/createAssets.ts +++ b/packages/open-next/src/build/createAssets.ts @@ -2,6 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { loadConfig } from "config/util.js"; +import { safeParseJsonFile } from "utils/safe-json-parse.js"; import logger from "../logger.js"; import type { TagCacheMetaFile } from "../types/cache.js"; import { isBinaryContentType } from "../utils/binary.js"; @@ -159,15 +160,20 @@ export function createCacheAssets(options: buildHelper.BuildOptions) { // Generate cache file Object.entries(cacheFilesPath).forEach(([cacheFilePath, files]) => { const cacheFileMeta = files.meta - ? JSON.parse(fs.readFileSync(files.meta, "utf8")) + ? safeParseJsonFile(fs.readFileSync(files.meta, "utf8"), cacheFilePath) : undefined; + const cacheJson = files.json + ? safeParseJsonFile(fs.readFileSync(files.json, "utf8"), cacheFilePath) + : undefined; + if ((files.meta && !cacheFileMeta) || (files.json && !cacheJson)) { + logger.warn(`Skipping invalid cache file: ${cacheFilePath}`); + return; + } const cacheFileContent = { type: files.body ? "route" : files.json ? "page" : "app", meta: cacheFileMeta, html: files.html ? fs.readFileSync(files.html, "utf8") : undefined, - json: files.json - ? JSON.parse(fs.readFileSync(files.json, "utf8")) - : undefined, + json: cacheJson, rsc: files.rsc ? fs.readFileSync(files.rsc, "utf8") : undefined, body: files.body ? fs @@ -203,7 +209,7 @@ export function createCacheAssets(options: buildHelper.BuildOptions) { () => true, ({ absolutePath, relativePath }) => { const fileContent = fs.readFileSync(absolutePath, "utf8"); - const fileData = JSON.parse(fileContent); + const fileData = safeParseJsonFile(fileContent, absolutePath); fileData?.tags?.forEach((tag: string) => { metaFiles.push({ tag: { S: path.posix.join(buildId, tag) }, @@ -227,8 +233,8 @@ export function createCacheAssets(options: buildHelper.BuildOptions) { absolutePath.endsWith(".meta") && !isFileSkipped(relativePath), ({ absolutePath, relativePath }) => { const fileContent = fs.readFileSync(absolutePath, "utf8"); - const fileData = JSON.parse(fileContent); - if (fileData.headers?.["x-next-cache-tags"]) { + const fileData = safeParseJsonFile(fileContent, absolutePath); + if (fileData?.headers?.["x-next-cache-tags"]) { fileData.headers["x-next-cache-tags"] .split(",") .forEach((tag: string) => { diff --git a/packages/open-next/src/utils/safe-json-parse.ts b/packages/open-next/src/utils/safe-json-parse.ts new file mode 100644 index 000000000..79fd74944 --- /dev/null +++ b/packages/open-next/src/utils/safe-json-parse.ts @@ -0,0 +1,16 @@ +import logger from "../logger.js"; + +export function safeParseJsonFile( + input: string, + filePath: string, + fallback?: T, +): T | undefined { + try { + return JSON.parse(input); + } catch (err) { + logger.warn( + `Failed to parse JSON file "${filePath}". Error: ${(err as Error).message}`, + ); + return fallback; + } +}