Trace Next.js cache tag relationships and catch static cache API mistakes before they reach production.
Use it when migrating cache code to Next.js 16, reviewing a tag rename, or adding cache checks to CI. It finds invalidations without observed tag producers (with spelling/case suggestions), deprecated invalidation calls, oversized tag lists, and direct request-data reads inside shared caches. It does not diagnose every stale-page or Router Cache problem.
A local TypeScript AST analyzer for Next.js 16 App Router. It provides a CLI, typed JavaScript API, ESLint integration, and text/JSON/Markdown/SARIF/standalone HTML reports. No telemetry and no application code execution.
Node.js 20.19+ (ESLint 10 requires 22.13+ on the Node 22 line).
npm install --save-dev next-cache-trace
npx --no-install next-cache-trace audit . --fail-on none
npx --no-install next-cache-trace audit . --format markdown --output artifacts/cache.mdFor a guided first setup in a Next.js app:
npx --no-install next-cache-trace init . --dry-run
npx --no-install next-cache-trace init .
npx --no-install next-cache-trace doctor .
npx --no-install next-cache-trace audit . --explaininit adds a cache:check script and a config with minFiles: 1. It preserves existing scripts/configuration, is safe to rerun, and does not install dependencies or change Next configuration. Run it in the app directory, not a workspace container. doctor reads the installed Next version and checks setup, scope and unresolved coverage without executing the app. Setup errors exit 2; warnings exit 0. It does not enforce the audit finding threshold.
--explain adds source excerpts from the analyzed snapshot, reasons and conditional solution examples to text or Markdown output. HTML includes expandable explanations; JSON includes structured finding.explanation fields. Examples are guidance, not automatic fixes. NCT006 explains all three invalidation choices and whether a Server Action was recognized. Reports now include short source excerpts as well as paths and tags.
Version 0.3.0 also follows explicitly named constant re-exports and local import/export lists. For gradual CI adoption, review the initial report, then create a baseline:
npx --no-install next-cache-trace audit . --baseline cache-baseline.json --update-baseline
npx --no-install next-cache-trace audit . --baseline cache-baseline.json --fail-on warning
npx --no-install next-cache-trace audit . --tag postsBaseline comparison preserves all findings and applies the CI threshold only to new ones. Commit the reviewed baseline; update it deliberately. Add --min-files 1 --require-cache-usage to detect an unexpectedly empty scan. --tag selects the text display; CI still evaluates the full report.
Upgrade note: report schema is now 0.4. Baselines from 0.2.x/schema 0.3 must be reviewed and regenerated; named re-export support can change observed relationships. Existing exit thresholds and rule defaults are unchanged. --explain works with text/Markdown and cannot be combined with --tag.
npm ci
npm run verify
npm pack --pack-destination artifacts
npm install --save-dev /absolute/path/to/next-cache-trace/artifacts/next-cache-trace-0.3.0.tgzFor CI, pin the installed version in your lockfile and use --fail-on warning after reviewing initial findings. A local tarball generated by npm pack is also supported.
import { cacheTag, cacheLife, updateTag } from 'next/cache';
export async function getPosts() {
'use cache';
cacheTag('posts');
cacheLife('hours');
return [];
}
export async function save() {
updateTag('post'); // NCT001: observed producer is "posts", not "post".
}| Rule | Default | Meaning |
|---|---|---|
| NCT001 | warning | Invalidation has no observed producer in the scanned files. |
| NCT002 | error | Direct imported cookies()/headers() call inside a shared/remote cached function. |
| NCT003 | warning | Literal tag shared by multiple top-level route areas; a review signal. |
| NCT004 | error | Cache Components usage with statically disabled/missing configuration. |
| NCT005 | info | Cached function has no direct cacheLife call; default lifetime may be intentional. |
| NCT006 | warning | One-argument revalidateTag call is deprecated in Next.js 16. |
| NCT007 | warning | Literal tag exceeds 256 UTF-16 code units. |
| NCT008 | warning | Fully known tag list exceeds 128 items in one call/array. |
| NCT009 | off (opt-in info) | Review whether a Server Action using revalidateTag(tag, 'max') needs immediate read-your-own-writes. Valid usage, not an error. |
| NCT900 | info | Dynamic tag or unresolved options; relationship not inferred. |
| NCT901 | info | Exported Next configuration could not be statically resolved. |
| NCT902 | error | Source parsing failed. |
Named aliases, namespace imports of Next APIs, shadowed bindings, imported const values, object/array fields, evidence chains, function directives and file-level exported functions are handled. Constant values can travel through export { TAG as POSTS } from './tags' and local import/export lists, with each hop recorded. Wildcard/default/namespace exports, wrappers, mutable containers and dynamic expressions remain unresolved. Producers include literal or safely resolved cacheTag, fetch(..., { next: { tags } }) and unstable_cache(..., ..., { tags }).
Reading draftMode().isEnabled is allowed by current Next.js documentation. Private caches allow request APIs. This tool does not check draft-mode enable/disable mutations or experimental private-cache configuration.
node bin/next-cache-trace.js audit /path/to/app --format json
node bin/next-cache-trace.js audit /path/to/app --format sarif --output artifacts/cache.sarif
node bin/next-cache-trace.js audit /path/to/app --format html --output artifacts/cache.html
node bin/next-cache-trace.js audit /path/to/app --fail-on warningExit 0: threshold not reached. Exit 1: findings reached the threshold. Exit 2: bad arguments, file I/O or configuration error. Default threshold is error; none disables finding-based failure, not tool errors. Existing output files need --force.
Put next-cache-trace.config.json in the project being audited:
{
"include": ["app/**", "src/**", "lib/**"],
"exclude": ["src/generated/**"],
"rules": { "NCT003": "off", "NCT005": "info" }
}Use cacheComponents: true here only to provide known configuration when Next config is dynamic. --config resolves relative to the audited project. --exclude and --ignore-rule are repeatable. Glob paths use forward slashes.
// next-cache-trace-disable-next-line NCT001 -- producer lives in another service
updateTag('external');Ignored findings remain in the relationship graph. Default exclusions cover node_modules, .next, build outputs, declaration files, .test., .spec. and config files. Symlinks are not followed.
Install next-cache-trace, eslint, and typescript-eslint as dev dependencies. The companion eslint-plugin-next-cache-trace is an optional packaging alias and also requires the main package installed explicitly.
// eslint.config.mjs
import tseslint from 'typescript-eslint';
import trace from 'next-cache-trace/eslint';
// Alternatively: import trace from 'eslint-plugin-next-cache-trace';
export default [{
files: ['**/*.{js,jsx,ts,tsx}'],
languageOptions: { parser: tseslint.parser },
...trace.configs.project,
settings: {
'next-cache-trace': { projectRoot: import.meta.dirname }
}
}];recommended runs file-local rules only (NCT002/NCT005/NCT006/NCT007/NCT008/NCT900), suitable for fast editor feedback. project enables all default-on rules and requires projectRoot for cross-file NCT001/NCT003 and config inference. Both leave NCT009 off. To enable that advisory, add rules: { ...trace.configs.project.rules, 'next-cache-trace/NCT009': 'warn' } to the project config above. For the CLI, use "rules": { "NCT009": "info" } in the JSON config.
Project mode reads one fresh source snapshot and resolves supported imports and named constant re-exports within that snapshot; unsaved text in the current editor buffer overrides its disk version. Symlinks are not followed and nested Next apps must be excluded or audited separately.
ESLint maps informational findings to warnings because ESLint has no info level. Use normal ESLint rule severity settings and disable comments there. CLI JSON config controls CLI severity.
Compiler aliases use baseUrl/paths from the app's tsconfig.json or jsconfig.json. Only relative config inheritance within the audited root is supported; external or package-based extends fail explicitly. Audit each Next app separately. Direct named imports must resolve to scanned source files; excluded files do not contribute evidence.
This is not a runtime cache tracer. It cannot prove hit/miss behavior, freshness, whether a branch executes, or whether a tag is intentionally shared. An observed literal edge is source evidence, not an execution trace.
It does not follow export *, default/namespace exports, re-exported Next API functions, imported wrappers, indirect helper calls, arbitrary variable flow, CommonJS require bindings, MDX, or route consumer call graphs. Named constant export chains stop at cycles, conflicting explicit exports, missing/excluded sources or a 64-module/name resolution limit. Nested ordinary functions are analyzed separately and are not assumed to execute inside the outer cache. Code using those patterns can have missed findings. Dynamic Next config is reported as unknown rather than executed.
NCT001 is deliberately observational: external/dynamic producers and exclusions may explain it. Spelling suggestions are hints, not inferred links or automatic fixes. Zero findings does not establish correctness, especially when the scan found no cache boundaries. The graph retains literal source evidence even when NCT007/NCT008 reports that those tags exceed supported limits; it is not a list of tags proven to be registered at runtime.
Targets Next.js 16 App Router. Pages Router, Edge-specific behavior, storage backends and live overlays are outside this release.
npm test
npm run verifyverify builds strict TypeScript, runs behavioral tests, installs both tarballs in a temporary consumer, exercises the installed CLI/ESLint plugin, then inspects package contents. The clean-install check needs registry access.
Start with the pilot guide to install a reviewed tarball, act on findings, and decide whether this tool earns a place in your CI. See also rules, architecture, and framework notes. The Windows/macOS/Linux and Next minor-version matrices are defined in GitHub Actions; local success is not evidence those hosted jobs ran.
MIT.