2026-08-06 09f01778d5a034e2af50ae05da8908f7b6a871c8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
 * Next.js registry entry.
 *
 * Next takes the generic tag strategy: the App Router's root layout renders
 * `<html>…<body>` in JSX, so the marker-wrapped script block goes in there
 * verbatim. Nothing about injection differs from a plain Vite app, which is
 * why live-inject.mjs never had a Next branch. The entry exists so the
 * registry can name what it is looking at.
 */
 
import { fileExists, findConfigFile, hasAnyDependency } from './detect-utils.mjs';
 
const NEXT_CONFIG_RE = /^next\.config\.(?:js|mjs|cjs|ts|mts|cts)$/;
 
const ROUTER_ENTRY_CANDIDATES = [
  'app/layout.tsx', 'app/layout.jsx', 'app/layout.ts', 'app/layout.js',
  'src/app/layout.tsx', 'src/app/layout.jsx', 'src/app/layout.ts', 'src/app/layout.js',
  'pages/_app.tsx', 'pages/_app.jsx', 'pages/_app.ts', 'pages/_app.js',
  'pages/_document.tsx', 'pages/_document.jsx',
  'src/pages/_app.tsx', 'src/pages/_app.jsx',
];
 
export function detectNextProject(cwd = process.cwd()) {
  const configFile = findConfigFile(cwd, NEXT_CONFIG_RE);
  if (configFile) return { configFile, via: 'config' };
  if (hasAnyDependency(cwd, ['next'])) return { configFile: null, via: 'package' };
  // Next's file conventions are distinctive enough to stand alone: a root
  // `app/layout.*` or `pages/_app.*` is not a shape other bundlers produce.
  const entry = ROUTER_ENTRY_CANDIDATES.find((rel) => fileExists(cwd, rel));
  if (entry) return { configFile: null, via: 'router-entry', entry };
  return null;
}
 
export const nextjs = {
  name: 'nextjs',
 
  detect(cwd) {
    return detectNextProject(cwd);
  },
 
  inject: { kind: 'tag' },
 
  source: {
    extensions: ['.tsx', '.jsx'],
    preview: 'source',
    styleMode: 'scoped',
    commentSyntax: 'jsx',
  },
};