diff --git a/src/adapters/index.ts b/src/adapters/index.ts index b4b83e82..2a38dae2 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -1,5 +1,4 @@ import { readFile, rm } from "node:fs/promises"; -import { createRequire } from "node:module"; import { sep } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; @@ -19,7 +18,7 @@ import { writeFileRecursive, } from "../lib/file"; import { stringify } from "../lib/json"; -import { findPackageJson, readPackageJson } from "../lib/packageJson"; +import { readPackageJson } from "../lib/packageJson"; import { addPreview, getPreviews, @@ -138,18 +137,6 @@ export async function getJsFileExtension(): Promise { return (await checkIsTypeScriptProject()) ? "ts" : "js"; } -export async function getInstalledMajor(packageName: string): Promise { - const require = createRequire(await findPackageJson()); - try { - const { version } = require(`${packageName}/package.json`); - const major = Number.parseInt(version.split(".")[0]); - return Number.isNaN(major) ? Infinity : major; - } catch { - // Not installed yet, so assume the newest major. - return Infinity; - } -} - export abstract class Adapter { abstract readonly id: string; abstract readonly environmentEnvVarName: string; diff --git a/src/adapters/nextjs.ts b/src/adapters/nextjs.ts index 42707749..ca154a33 100644 --- a/src/adapters/nextjs.ts +++ b/src/adapters/nextjs.ts @@ -7,13 +7,12 @@ import { pascalCase } from "change-case"; import { Adapter, checkSourceContains, - getInstalledMajor, getJsFileExtension, type PageFile, writeFileIfMissing, } from "."; import { exists, writeFileRecursive } from "../lib/file"; -import { addDependencies, getNpmPackageVersion } from "../lib/packageJson"; +import { addDependencies, getInstalledMajor, getNpmPackageVersion } from "../lib/packageJson"; import { dedent, formatObjectKey } from "../lib/string"; import { checkIsTypeScriptProject, findProjectRoot } from "../project"; import { @@ -48,6 +47,7 @@ export class NextJsAdapter extends Adapter { const extension = await getJsFileExtension(); const typescript = await checkIsTypeScriptProject(); const appRouter = await checkUsesAppRouter(); + const supportsCacheLife = (await getInstalledMajor("next")) >= 16; await writeFileIfMissing( new URL(`prismicio.${extension}`, sourceRoot), @@ -81,7 +81,7 @@ export class NextJsAdapter extends Adapter { if (appRouter) { await writeFileIfMissing( new URL(`app/api/revalidate/route.${extension}`, sourceRoot), - revalidateRouteTemplate({ supportsCacheLife: (await getInstalledMajor("next")) >= 16 }), + revalidateRouteTemplate({ supportsCacheLife }), ); } } diff --git a/src/lib/packageJson.ts b/src/lib/packageJson.ts index 5afddd5a..4aaf16e5 100644 --- a/src/lib/packageJson.ts +++ b/src/lib/packageJson.ts @@ -1,4 +1,5 @@ import { readFile, writeFile } from "node:fs/promises"; +import { createRequire } from "node:module"; import { fileURLToPath } from "node:url"; import detectIndent from "detect-indent"; @@ -59,6 +60,20 @@ export async function addDependencies(dependencies: Record): Pro }); } +export async function getInstalledMajor(packageName: string): Promise { + const require = createRequire(await findPackageJson()); + try { + const { version } = require(`${packageName}/package.json`); + const major = Number.parseInt(version.split(".")[0]); + return Number.isNaN(major) ? Infinity : major; + } catch { + const { dependencies, devDependencies, peerDependencies } = await readPackageJson(); + const range = { ...peerDependencies, ...dependencies, ...devDependencies }[packageName] ?? ""; + const major = /^[\^~]?(\d+)/.exec(range)?.[1]; + return major ? Number(major) : Infinity; + } +} + // Returns the packages whose major version changed between two reads of package.json. export function getMajorDependencyUpdates( before: PackageJson, diff --git a/test/gen-setup.test.ts b/test/gen-setup.test.ts index 2594b30c..6962757f 100644 --- a/test/gen-setup.test.ts +++ b/test/gen-setup.test.ts @@ -289,6 +289,25 @@ it("works when Next.js is declared but not installed", async ({ expect, project, await expect(project).toHaveFile("app/api/revalidate/route.js"); }); +it("generates a Next.js 15 revalidate route before Next.js is installed", async ({ + expect, + project, + prismic, +}) => { + await rm(new URL("node_modules/next/", project), { recursive: true }); + await writeFile( + new URL("package.json", project), + JSON.stringify({ dependencies: { next: "^15.0.0" } }), + ); + + const { stderr, exitCode } = await prismic("gen", ["setup", "--no-install"]); + expect(exitCode, stderr).toBe(0); + + await expect(project).toHaveFile("app/api/revalidate/route.js", { + contains: 'revalidateTag("prismic");', + }); +}); + it( "prints SvelteKit instructions when the project already has a root layout", { timeout: 30_000 },