43 lines
1.5 KiB
JavaScript
43 lines
1.5 KiB
JavaScript
export const metadata = {
|
|
id: 'force-dynamic',
|
|
title: "export const dynamic = 'force-dynamic'",
|
|
severity: 'medium',
|
|
billingDimension: 'function-duration',
|
|
trafficIndependent: false,
|
|
description:
|
|
"force-dynamic disables static + ISR rendering. The route runs the function on every request. Sometimes necessary (cookies, headers, real-time data), often a habit that costs function-duration and edge-requests at scale.",
|
|
fix:
|
|
"Audit the route. If dynamic behavior comes from cookies()/headers()/searchParams, force-dynamic may be redundant — Next infers dynamic automatically. Consider revalidate / 'use cache' / generateStaticParams if any portion can be pre-rendered.",
|
|
citations: [
|
|
'https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config',
|
|
],
|
|
excludeGlobs: ['node_modules/**', '.next/**', 'dist/**', '__tests__/**'],
|
|
includeGlobs: ['**/route.{ts,tsx,js,jsx}', '**/page.{ts,tsx,js,jsx}'],
|
|
};
|
|
|
|
const RE = /export\s+const\s+dynamic\s*=\s*["']force-dynamic["']/;
|
|
|
|
export function scan({ files }) {
|
|
const out = [];
|
|
for (const { path, content } of files) {
|
|
if (!isApplicable(path)) continue;
|
|
const m = RE.exec(content);
|
|
if (m) {
|
|
out.push({
|
|
pattern: metadata.id,
|
|
file: path,
|
|
line: lineOf(content, m.index),
|
|
evidence: 'export const dynamic = "force-dynamic"',
|
|
trafficIndependent: metadata.trafficIndependent,
|
|
});
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
import { lineOf } from '../util.mjs';
|
|
|
|
function isApplicable(path) {
|
|
return /(\/route|\/page)\.(tsx?|jsx?)$/.test(path);
|
|
}
|