Skip to content

Commit 0f5c1e0

Browse files
authored
detect version channel (#66)
1 parent aedeae4 commit 0f5c1e0

File tree

5 files changed

+374
-6
lines changed

5 files changed

+374
-6
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { readFileSync, existsSync } from "fs"
2+
import { join } from "path"
3+
4+
export interface ChannelDetectionResult {
5+
isBeta: boolean
6+
isCanary: boolean
7+
currentVersion: string | null
8+
}
9+
10+
/**
11+
* Detects the Next.js channel (beta/canary/stable) from a project's package.json
12+
* @param projectPath - Path to the Next.js project directory
13+
* @returns Channel detection result with isBeta, isCanary, and currentVersion
14+
*/
15+
export function detectProjectChannel(projectPath: string): ChannelDetectionResult {
16+
const packageJsonPath = join(projectPath, "package.json")
17+
18+
if (!existsSync(packageJsonPath)) {
19+
return { isBeta: false, isCanary: false, currentVersion: null }
20+
}
21+
22+
try {
23+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"))
24+
const nextVersion = packageJson.dependencies?.next || packageJson.devDependencies?.next
25+
26+
if (!nextVersion) {
27+
return { isBeta: false, isCanary: false, currentVersion: null }
28+
}
29+
30+
const isBeta = nextVersion.includes("beta") || nextVersion.includes("16.0.0-beta")
31+
const isCanary = nextVersion === "canary" || nextVersion.includes("canary")
32+
33+
return { isBeta, isCanary, currentVersion: nextVersion }
34+
} catch (error) {
35+
console.warn("Failed to parse package.json, assuming not on beta/canary")
36+
return { isBeta: false, isCanary: false, currentVersion: null }
37+
}
38+
}
39+
40+
/**
41+
* Processes conditional template blocks based on channel detection
42+
* Supports {{IF_BETA_CHANNEL}} blocks
43+
* @param template - Template string with conditional blocks
44+
* @param isBeta - Whether the project is on beta channel
45+
* @returns Processed template with conditional blocks resolved
46+
*/
47+
export function processConditionalBlocks(template: string, isBeta: boolean): string {
48+
let result = template
49+
50+
// Process IF_BETA_CHANNEL blocks
51+
if (isBeta) {
52+
// Keep content, remove markers
53+
result = result.replace(/\{\{IF_BETA_CHANNEL\}\}/g, "")
54+
result = result.replace(/\{\{\/IF_BETA_CHANNEL\}\}/g, "")
55+
} else {
56+
// Remove entire block including content
57+
result = result.replace(/\{\{IF_BETA_CHANNEL\}\}.*?\{\{\/IF_BETA_CHANNEL\}\}/gs, "")
58+
}
59+
60+
return result
61+
}
62+

src/prompts/upgrade-nextjs-16-prompt.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -328,9 +328,18 @@ After the codemod runs, check for any remaining issues it might have missed:
328328
File: next.config.js
329329
Check: `serverComponentsExternalPackages` in experimental config
330330
Action: Move out of experimental - this is now a top-level config option
331-
331+
332332
**📖 For code examples, see: `nextjs16://migration/examples` (Config Migration Examples)**
333333

334+
{{IF_BETA_CHANNEL}}**J. Beta to Stable Migration (REQUIRED for beta channel users)**
335+
336+
You are currently upgrading to Next.js 16 **beta** channel. When Next.js 16 **stable** is released, you will need to apply additional config migrations:
337+
338+
{{BETA_TO_STABLE_GUIDE}}
339+
340+
**Key migration when stable is released**: `experimental.cacheLife` must be moved to top-level `cacheLife`{{/IF_BETA_CHANNEL}}
341+
342+
334343
**K. Edge Cases the Codemod May Miss**
335344
Review these manually:
336345

@@ -535,10 +544,6 @@ Report findings in this format:
535544
- [ ] Codemod upgraded Next.js, React, and React DOM to stable
536545
- [ ] Codemod upgraded React type definitions to stable
537546
- [ ] Codemod applied automatic fixes
538-
{{IF_REQUIRES_CANARY}}
539-
- [ ] (Optional) Upgraded to canary: `<pkg-manager> add next@canary`
540-
- [ ] (Optional) Upgraded eslint-config-next: `<pkg-manager> add -D eslint-config-next@canary`
541-
{{/IF_REQUIRES_CANARY}}
542547
- [ ] TypeScript upgraded if needed: `<pkg-manager> add -D typescript@latest`
543548
- [ ] Reviewed git diff for codemod changes
544549
- [ ] **Verified build: `<pkg-manager> run build` (if this passes, upgrade is complete!)**

src/prompts/upgrade-nextjs-16.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import { type InferSchema, type PromptMetadata } from "xmcp"
22
import { z } from "zod"
33
import { readResourceFile } from "../_internal/resource-path"
44
import { execSync } from "child_process"
5+
import {
6+
detectProjectChannel,
7+
processConditionalBlocks,
8+
} from "../_internal/nextjs-channel-detector"
59

610
export const schema = {
711
project_path: z
@@ -37,12 +41,18 @@ export default function getUpgradeNextjs16Prompt(args: InferSchema<typeof schema
3741
const upgradeChannel = "latest"
3842
const codemodCommandNote = `**Note**: Next.js 16 stable (version ${version}) is now available.`
3943

44+
// Detect if project is on beta/canary
45+
const { isBeta } = detectProjectChannel(projectPath)
46+
4047
let promptTemplate = readResourceFile("prompts/upgrade-nextjs-16-prompt.md")
4148

42-
// TODO: load next.js16 knowledge base resources
49+
// Replace basic template variables
4350
promptTemplate = promptTemplate.replace(/{{PROJECT_PATH}}/g, projectPath)
4451
promptTemplate = promptTemplate.replace(/{{UPGRADE_CHANNEL}}/g, upgradeChannel)
4552
promptTemplate = promptTemplate.replace(/{{CODEMOD_COMMAND}}/g, codemodCommandNote)
4653

54+
// Process conditional blocks based on project channel
55+
promptTemplate = processConditionalBlocks(promptTemplate, isBeta)
56+
4757
return promptTemplate
4858
}

test/prompts/prompts.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,18 @@ describe('Prompts Token Size', () => {
2929

3030
expect(estimatedTokens).toBeLessThan(10000)
3131
})
32+
33+
it('upgrade-nextjs-16 should not contain unprocessed template markers', () => {
34+
const result = getUpgradeNextjs16Prompt({ project_path: undefined })
35+
36+
// Verify all conditional blocks are processed (no leftover markers)
37+
expect(result).not.toContain('{{IF_BETA_CHANNEL}}')
38+
expect(result).not.toContain('{{/IF_BETA_CHANNEL}}')
39+
40+
// Verify basic template variables are replaced
41+
expect(result).not.toContain('{{PROJECT_PATH}}')
42+
expect(result).not.toContain('{{UPGRADE_CHANNEL}}')
43+
expect(result).not.toContain('{{CODEMOD_COMMAND}}')
44+
})
3245
})
3346

0 commit comments

Comments
 (0)