@clerk/upgrade 2.0.0-canary.v20260105222323 → 2.0.0-canary.v20260106195108

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "test-nextjs-catalog",
3
+ "version": "1.0.0",
4
+ "dependencies": {
5
+ "@clerk/nextjs": "catalog:",
6
+ "next": "^14.0.0",
7
+ "react": "^18.0.0"
8
+ }
9
+ }
@@ -0,0 +1,5 @@
1
+ lockfileVersion: '9.0'
2
+ settings:
3
+ autoInstallPeers: true
4
+ excludeLinksFromLockfile: false
5
+
@@ -0,0 +1,5 @@
1
+ import { SignIn } from '@clerk/nextjs';
2
+
3
+ export default function Home() {
4
+ return <SignIn />;
5
+ }
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { getOldPackageName, getTargetPackageName, loadConfig } from '../../config.js';
2
+ import { getAvailableReleases, getOldPackageName, getTargetPackageName, loadConfig } from '../../config.js';
3
3
  describe('loadConfig', () => {
4
4
  it('returns config with needsUpgrade: true for nextjs v6', async () => {
5
5
  const config = await loadConfig('nextjs', 6);
@@ -94,4 +94,35 @@ describe('getOldPackageName', () => {
94
94
  it('returns null for nextjs sdk (no rename)', () => {
95
95
  expect(getOldPackageName('nextjs')).toBeNull();
96
96
  });
97
+ });
98
+ describe('getAvailableReleases', () => {
99
+ it('returns an array of available releases', () => {
100
+ const releases = getAvailableReleases();
101
+ expect(Array.isArray(releases)).toBe(true);
102
+ expect(releases.length).toBeGreaterThan(0);
103
+ });
104
+ it('includes core-3 release', () => {
105
+ const releases = getAvailableReleases();
106
+ expect(releases).toContain('core-3');
107
+ });
108
+ it('includes core-2 release', () => {
109
+ const releases = getAvailableReleases();
110
+ expect(releases).toContain('core-2');
111
+ });
112
+ it('returns releases in reverse order (newest first)', () => {
113
+ const releases = getAvailableReleases();
114
+ expect(releases[0]).toBe('core-3');
115
+ expect(releases[1]).toBe('core-2');
116
+ });
117
+ });
118
+ describe('loadConfig with null version', () => {
119
+ it('returns config when release is explicitly provided', async () => {
120
+ const config = await loadConfig('nextjs', null, 'core-3');
121
+ expect(config).not.toBeNull();
122
+ expect(config.id).toBe('core-3');
123
+ });
124
+ it('returns null when no release is provided and version is null', async () => {
125
+ const config = await loadConfig('nextjs', null);
126
+ expect(config).toBeNull();
127
+ });
97
128
  });
@@ -45,6 +45,10 @@ describe('getSdkVersion', () => {
45
45
  const version = getSdkVersion('nextjs', getFixturePath('no-clerk'));
46
46
  expect(version).toBeNull();
47
47
  });
48
+ it('returns null for catalog: protocol versions', () => {
49
+ const version = getSdkVersion('nextjs', getFixturePath('nextjs-catalog'));
50
+ expect(version).toBeNull();
51
+ });
48
52
  });
49
53
  describe('getMajorVersion', () => {
50
54
  it('parses ^6.0.0 as version 6', () => {
@@ -62,6 +66,15 @@ describe('getMajorVersion', () => {
62
66
  it('returns null for invalid semver', () => {
63
67
  expect(getMajorVersion('invalid')).toBeNull();
64
68
  });
69
+ it('returns null for catalog: protocol', () => {
70
+ expect(getMajorVersion('catalog:')).toBeNull();
71
+ });
72
+ it('returns null for catalog:default', () => {
73
+ expect(getMajorVersion('catalog:default')).toBeNull();
74
+ });
75
+ it('returns null for workspace: protocol', () => {
76
+ expect(getMajorVersion('workspace:*')).toBeNull();
77
+ });
65
78
  });
66
79
  describe('normalizeSdkName', () => {
67
80
  it('returns null for null input', () => {
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import meow from 'meow';
3
- import { getOldPackageName, getTargetPackageName, loadConfig } from './config.js';
3
+ import { getAvailableReleases, getOldPackageName, getTargetPackageName, loadConfig } from './config.js';
4
4
  import { createSpinner, promptConfirm, promptSelect, renderComplete, renderConfig, renderError, renderHeader, renderNewline, renderScanResults, renderSuccess, renderText, renderWarning } from './render.js';
5
5
  import { runCodemods, runScans } from './runner.js';
6
6
  import { detectSdk, getSdkVersion, getSupportedSdks, normalizeSdkName } from './util/detect-sdk.js';
@@ -104,14 +104,41 @@ async function main() {
104
104
  const currentVersion = getSdkVersion(sdk, options.dir);
105
105
  const packageManager = detectPackageManager(options.dir);
106
106
 
107
- // Step 3: Load version config
108
- const config = await loadConfig(sdk, currentVersion, options.release);
107
+ // Step 3: If version couldn't be detected and no release specified, prompt user
108
+ let release = options.release;
109
+ if (currentVersion === null && !release) {
110
+ const availableReleases = getAvailableReleases();
111
+ if (availableReleases.length === 0) {
112
+ renderError('No upgrade configurations found.');
113
+ process.exit(1);
114
+ }
115
+ renderWarning(`Could not detect your @clerk/${sdk} version (you may be using catalog: protocol or a non-standard version specifier).`);
116
+ renderNewline();
117
+ if (!isInteractive) {
118
+ renderError('Please provide --release flag in non-interactive mode.');
119
+ renderText('Available releases: ' + availableReleases.join(', '));
120
+ process.exit(1);
121
+ }
122
+ const releaseOptions = availableReleases.map(r => ({
123
+ label: r.replace('-', ' ').replace(/\b\w/g, c => c.toUpperCase()),
124
+ value: r
125
+ }));
126
+ release = await promptSelect('Which upgrade would you like to perform?', releaseOptions);
127
+ if (!release) {
128
+ renderError('No release selected. Exiting.');
129
+ process.exit(1);
130
+ }
131
+ renderNewline();
132
+ }
133
+
134
+ // Step 4: Load version config
135
+ const config = await loadConfig(sdk, currentVersion, release);
109
136
  if (!config) {
110
137
  renderError(`No upgrade path found for @clerk/${sdk}. Your version may be too old for this upgrade tool.`);
111
138
  process.exit(1);
112
139
  }
113
140
 
114
- // Step 4: Display configuration
141
+ // Step 5: Display configuration
115
142
  renderConfig({
116
143
  sdk,
117
144
  currentVersion,
@@ -127,7 +154,7 @@ async function main() {
127
154
  }
128
155
  console.log('');
129
156
 
130
- // Step 5: Handle upgrade status
157
+ // Step 6: Handle upgrade status
131
158
  if (options.skipUpgrade) {
132
159
  renderText('Skipping package upgrade (--skip-upgrade flag)', 'yellow');
133
160
  renderNewline();
@@ -137,7 +164,7 @@ async function main() {
137
164
  await performUpgrade(sdk, packageManager, config, options);
138
165
  }
139
166
 
140
- // Step 6: Run codemods
167
+ // Step 7: Run codemods
141
168
  if (config.codemods?.length > 0) {
142
169
  renderText(`Running ${config.codemods.length} codemod(s)...`, 'blue');
143
170
  await runCodemods(config, sdk, options);
@@ -145,14 +172,14 @@ async function main() {
145
172
  renderNewline();
146
173
  }
147
174
 
148
- // Step 7: Run scans
175
+ // Step 8: Run scans
149
176
  if (config.changes?.length > 0) {
150
177
  renderText('Scanning for additional breaking changes...', 'blue');
151
178
  const results = await runScans(config, sdk, options);
152
179
  renderScanResults(results, config.docsUrl);
153
180
  }
154
181
 
155
- // Step 8: Done
182
+ // Step 9: Done
156
183
  renderComplete(sdk, config.docsUrl);
157
184
  }
158
185
  async function performUpgrade(sdk, packageManager, config, options) {
package/dist/config.js CHANGED
@@ -4,6 +4,11 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
4
4
  import matter from 'gray-matter';
5
5
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
6
  const VERSIONS_DIR = path.join(__dirname, 'versions');
7
+ export function getAvailableReleases() {
8
+ return fs.readdirSync(VERSIONS_DIR, {
9
+ withFileTypes: true
10
+ }).filter(d => d.isDirectory()).map(d => d.name).filter(name => fs.existsSync(path.join(VERSIONS_DIR, name, 'index.js'))).sort().reverse();
11
+ }
7
12
  export async function loadConfig(sdk, currentVersion, release) {
8
13
  const versionDirs = fs.readdirSync(VERSIONS_DIR, {
9
14
  withFileTypes: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clerk/upgrade",
3
- "version": "2.0.0-canary.v20260105222323",
3
+ "version": "2.0.0-canary.v20260106195108",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/clerk/javascript.git",