@clerk/upgrade 2.0.0-snapshot.v20251204175016 → 2.0.0-snapshot.v20251211120550
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.
- package/README.md +35 -5
- package/dist/__tests__/fixtures/expo-old-package/package-lock.json +5 -0
- package/dist/__tests__/fixtures/expo-old-package/package.json +10 -0
- package/dist/__tests__/fixtures/expo-old-package/src/App.tsx +14 -0
- package/dist/__tests__/fixtures/nextjs-v6/package.json +9 -0
- package/dist/__tests__/fixtures/nextjs-v6/pnpm-lock.yaml +2 -0
- package/dist/__tests__/fixtures/nextjs-v6/src/app.tsx +17 -0
- package/dist/__tests__/fixtures/nextjs-v7/package.json +9 -0
- package/dist/__tests__/fixtures/nextjs-v7/pnpm-lock.yaml +2 -0
- package/dist/__tests__/fixtures/nextjs-v7/src/app.tsx +16 -0
- package/dist/__tests__/fixtures/no-clerk/package.json +7 -0
- package/dist/__tests__/fixtures/react-v6/package.json +8 -0
- package/dist/__tests__/fixtures/react-v6/src/App.tsx +19 -0
- package/dist/__tests__/fixtures/react-v6/yarn.lock +2 -0
- package/dist/__tests__/helpers/create-fixture.js +56 -0
- package/dist/__tests__/integration/cli.test.js +275 -0
- package/dist/__tests__/integration/config.test.js +97 -0
- package/dist/__tests__/integration/detect-sdk.test.js +100 -0
- package/dist/__tests__/integration/runner.test.js +58 -0
- package/dist/cli.js +172 -44
- package/dist/codemods/__tests__/__fixtures__/transform-align-experimental-unstable-prefixes.fixtures.js +92 -0
- package/dist/codemods/__tests__/__fixtures__/transform-appearance-layout-to-options.fixtures.js +9 -0
- package/dist/codemods/__tests__/__fixtures__/transform-clerk-react-v6.fixtures.js +13 -0
- package/dist/codemods/__tests__/__fixtures__/transform-remove-deprecated-appearance-props.fixtures.js +63 -0
- package/dist/codemods/__tests__/__fixtures__/transform-themes-to-ui-themes.fixtures.js +41 -0
- package/dist/codemods/__tests__/transform-align-experimental-unstable-prefixes.test.js +15 -0
- package/dist/codemods/__tests__/transform-appearance-layout-to-options.test.js +15 -0
- package/dist/codemods/__tests__/transform-remove-deprecated-appearance-props.test.js +15 -0
- package/dist/codemods/__tests__/transform-themes-to-ui-themes.test.js +15 -0
- package/dist/codemods/index.js +67 -13
- package/dist/codemods/transform-align-experimental-unstable-prefixes.cjs +412 -0
- package/dist/codemods/transform-appearance-layout-to-options.cjs +65 -0
- package/dist/codemods/transform-clerk-react-v6.cjs +15 -7
- package/dist/codemods/transform-remove-deprecated-appearance-props.cjs +109 -0
- package/dist/codemods/transform-remove-deprecated-props.cjs +11 -32
- package/dist/codemods/transform-themes-to-ui-themes.cjs +65 -0
- package/dist/config.js +145 -0
- package/dist/render.js +170 -0
- package/dist/runner.js +98 -0
- package/dist/util/detect-sdk.js +125 -0
- package/dist/util/package-manager.js +94 -0
- package/dist/versions/core-3/changes/clerk-expo-package-rename.md +23 -0
- package/dist/versions/core-3/changes/clerk-react-package-rename.md +22 -0
- package/dist/versions/core-3/index.js +40 -0
- package/package.json +2 -8
- package/dist/app.js +0 -177
- package/dist/components/Codemod.js +0 -149
- package/dist/components/Command.js +0 -56
- package/dist/components/Header.js +0 -11
- package/dist/components/SDKWorkflow.js +0 -278
- package/dist/components/Scan.js +0 -180
- package/dist/components/UpgradeSDK.js +0 -116
- package/dist/util/expandable-list.js +0 -173
- package/dist/util/get-clerk-version.js +0 -22
- package/dist/util/guess-framework.js +0 -69
package/README.md
CHANGED
|
@@ -27,25 +27,55 @@
|
|
|
27
27
|
|
|
28
28
|
## Getting Started
|
|
29
29
|
|
|
30
|
-
A
|
|
30
|
+
A CLI that detects your installed Clerk SDK, upgrades packages to the next major release, runs codemods, and scans your codebase for breaking changes.
|
|
31
31
|
|
|
32
32
|
### Prerequisites
|
|
33
33
|
|
|
34
|
-
- Node.js `>=
|
|
34
|
+
- Node.js `>=20.9.0`
|
|
35
|
+
- pnpm, npm, or yarn installed in the target project
|
|
35
36
|
|
|
36
37
|
## Usage
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
Run the CLI from the root of the project you want to upgrade:
|
|
39
40
|
|
|
40
41
|
```sh
|
|
42
|
+
# with pnpm
|
|
43
|
+
pnpm dlx @clerk/upgrade
|
|
44
|
+
|
|
45
|
+
# with npm
|
|
41
46
|
npx @clerk/upgrade
|
|
47
|
+
|
|
48
|
+
# if installed locally
|
|
49
|
+
pnpm clerk-upgrade
|
|
42
50
|
```
|
|
43
51
|
|
|
44
|
-
Fill out the
|
|
52
|
+
Fill out the prompts and the CLI will:
|
|
53
|
+
|
|
54
|
+
- Detect your Clerk SDK and version (or prompt for `--sdk`)
|
|
55
|
+
- Upgrade packages (including renames such as `@clerk/clerk-react` → `@clerk/react`)
|
|
56
|
+
- Run codemods for the selected release
|
|
57
|
+
- Scan your files for breaking changes and print a report
|
|
58
|
+
|
|
59
|
+
### CLI options
|
|
60
|
+
|
|
61
|
+
- `--sdk` — SDK to upgrade (e.g., `nextjs`, `react`, `expo`); required in non-interactive runs if detection fails
|
|
62
|
+
- `--dir` — directory to scan (default: current working directory)
|
|
63
|
+
- `--glob` — glob of files for codemods (default: `**/*.(js|jsx|ts|tsx|mjs|cjs)`)
|
|
64
|
+
- `--ignore` — extra globs to ignore during scans (repeatable)
|
|
65
|
+
- `--release` — target release (e.g., `core-3`); otherwise auto-selected from installed versions
|
|
66
|
+
- `--skip-upgrade` — skip installing/updating packages
|
|
67
|
+
- `--skip-codemods` — skip codemod execution
|
|
68
|
+
- `--dry-run` — show what would change without writing or installing
|
|
69
|
+
|
|
70
|
+
### Releases
|
|
71
|
+
|
|
72
|
+
- `core-3`: upgrades Next.js 6 → 7, React 5 → 7, Expo 2 → 3, React Router 2 → 3, TanStack React Start 0 → 1, Astro 2 → 3, Nuxt 2 → 3, Vue 2 → 3; includes package renames for React/Expo
|
|
73
|
+
|
|
74
|
+
The CLI selects the appropriate release automatically based on the installed major version, or you can pin a release with `--release` (currently `core-3`).
|
|
45
75
|
|
|
46
76
|
### Caveats
|
|
47
77
|
|
|
48
|
-
|
|
78
|
+
Scans rely on regular expressions, so some patterns (unusual imports, bound methods, indirect calls) can be missed. Codemods cover common upgrades, but you should still review the report, run your test suite, and verify the app before deploying.
|
|
49
79
|
|
|
50
80
|
The main thing that this tool will miss is cases where _unusual import patterns_ are used in your codebase. As an example, if Clerk made a breaking change to the `getAuth` function exported from `@clerk/nextjs`, `@clerk/upgrade` would likely look for something like `import { getAuth } from "@clerk/nextjs"` in order to detect whether you need to make some changes. If you were using your imports like `import * as ClerkNext from "@clerk/nextjs"`, you could use `getAuth` without it detecting it with its matcher.
|
|
51
81
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ClerkProvider, useAuth } from '@clerk/clerk-expo';
|
|
2
|
+
|
|
3
|
+
export default function App() {
|
|
4
|
+
return (
|
|
5
|
+
<ClerkProvider publishableKey='pk_test_xxx'>
|
|
6
|
+
<AuthStatus />
|
|
7
|
+
</ClerkProvider>
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function AuthStatus() {
|
|
12
|
+
const { isSignedIn } = useAuth();
|
|
13
|
+
return <Text>{isSignedIn ? 'Signed in' : 'Signed out'}</Text>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ClerkProvider, useAuth } from '@clerk/nextjs';
|
|
2
|
+
import { useUser } from '@clerk/clerk-react';
|
|
3
|
+
|
|
4
|
+
export default function App({ children }) {
|
|
5
|
+
return <ClerkProvider>{children}</ClerkProvider>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function UserProfile() {
|
|
9
|
+
const { isSignedIn } = useAuth();
|
|
10
|
+
const { user } = useUser();
|
|
11
|
+
|
|
12
|
+
if (!isSignedIn) {
|
|
13
|
+
return <div>Not signed in</div>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return <div>Hello, {user?.firstName}</div>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ClerkProvider, useAuth, useUser } from '@clerk/nextjs';
|
|
2
|
+
|
|
3
|
+
export default function App({ children }) {
|
|
4
|
+
return <ClerkProvider>{children}</ClerkProvider>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function UserProfile() {
|
|
8
|
+
const { isSignedIn } = useAuth();
|
|
9
|
+
const { user } = useUser();
|
|
10
|
+
|
|
11
|
+
if (!isSignedIn) {
|
|
12
|
+
return <div>Not signed in</div>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return <div>Hello, {user?.firstName}</div>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ClerkProvider, useUser } from '@clerk/react';
|
|
2
|
+
|
|
3
|
+
export default function App() {
|
|
4
|
+
return (
|
|
5
|
+
<ClerkProvider publishableKey='pk_test_xxx'>
|
|
6
|
+
<UserInfo />
|
|
7
|
+
</ClerkProvider>
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function UserInfo() {
|
|
12
|
+
const { user, isSignedIn } = useUser();
|
|
13
|
+
|
|
14
|
+
if (!isSignedIn) {
|
|
15
|
+
return <div>Please sign in</div>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return <div>Welcome, {user?.firstName}</div>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const FIXTURES_DIR = path.join(__dirname, '..', 'fixtures');
|
|
7
|
+
export function getFixturePath(fixtureName) {
|
|
8
|
+
return path.join(FIXTURES_DIR, fixtureName);
|
|
9
|
+
}
|
|
10
|
+
export function createTempFixture(fixtureName) {
|
|
11
|
+
const sourcePath = getFixturePath(fixtureName);
|
|
12
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `clerk-upgrade-test-${fixtureName}-`));
|
|
13
|
+
copyDirSync(sourcePath, tempDir);
|
|
14
|
+
return {
|
|
15
|
+
path: tempDir,
|
|
16
|
+
cleanup() {
|
|
17
|
+
fs.rmSync(tempDir, {
|
|
18
|
+
recursive: true,
|
|
19
|
+
force: true
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function copyDirSync(src, dest) {
|
|
25
|
+
fs.mkdirSync(dest, {
|
|
26
|
+
recursive: true
|
|
27
|
+
});
|
|
28
|
+
const entries = fs.readdirSync(src, {
|
|
29
|
+
withFileTypes: true
|
|
30
|
+
});
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
const srcPath = path.join(src, entry.name);
|
|
33
|
+
const destPath = path.join(dest, entry.name);
|
|
34
|
+
if (entry.isDirectory()) {
|
|
35
|
+
copyDirSync(srcPath, destPath);
|
|
36
|
+
} else {
|
|
37
|
+
fs.copyFileSync(srcPath, destPath);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function readFixtureFile(fixtureName, filePath) {
|
|
42
|
+
return fs.readFileSync(path.join(getFixturePath(fixtureName), filePath), 'utf8');
|
|
43
|
+
}
|
|
44
|
+
export function writeFixtureFile(tempPath, filePath, content) {
|
|
45
|
+
const fullPath = path.join(tempPath, filePath);
|
|
46
|
+
fs.mkdirSync(path.dirname(fullPath), {
|
|
47
|
+
recursive: true
|
|
48
|
+
});
|
|
49
|
+
fs.writeFileSync(fullPath, content, 'utf8');
|
|
50
|
+
}
|
|
51
|
+
export function readTempFile(tempPath, filePath) {
|
|
52
|
+
return fs.readFileSync(path.join(tempPath, filePath), 'utf8');
|
|
53
|
+
}
|
|
54
|
+
export function fileExists(tempPath, filePath) {
|
|
55
|
+
return fs.existsSync(path.join(tempPath, filePath));
|
|
56
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { createTempFixture, getFixturePath } from '../helpers/create-fixture.js';
|
|
6
|
+
|
|
7
|
+
// Toggle this to true to debug the CLI output during the test run
|
|
8
|
+
const DEBUG_OUTPUT = false;
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const CLI_PATH = path.resolve(__dirname, '../../cli.js');
|
|
11
|
+
function runCli(args = [], options = {}) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const child = spawn('node', [CLI_PATH, ...args], {
|
|
14
|
+
cwd: options.cwd || process.cwd(),
|
|
15
|
+
env: {
|
|
16
|
+
...process.env,
|
|
17
|
+
FORCE_COLOR: '0',
|
|
18
|
+
NO_COLOR: '1'
|
|
19
|
+
},
|
|
20
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
21
|
+
});
|
|
22
|
+
let stdout = '';
|
|
23
|
+
let stderr = '';
|
|
24
|
+
child.stdout.on('data', data => {
|
|
25
|
+
stdout += data.toString();
|
|
26
|
+
if (DEBUG_OUTPUT) {
|
|
27
|
+
console.log(data.toString());
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
child.stderr.on('data', data => {
|
|
31
|
+
stderr += data.toString();
|
|
32
|
+
if (DEBUG_OUTPUT) {
|
|
33
|
+
console.error(data.toString());
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Send input if provided (for interactive prompts)
|
|
38
|
+
if (options.input) {
|
|
39
|
+
child.stdin.write(options.input);
|
|
40
|
+
child.stdin.end();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Set timeout to kill the process
|
|
44
|
+
const timeout = setTimeout(() => {
|
|
45
|
+
child.kill('SIGTERM');
|
|
46
|
+
resolve({
|
|
47
|
+
stdout,
|
|
48
|
+
stderr,
|
|
49
|
+
exitCode: null,
|
|
50
|
+
timedOut: true
|
|
51
|
+
});
|
|
52
|
+
}, options.timeout || 10000);
|
|
53
|
+
child.on('close', exitCode => {
|
|
54
|
+
clearTimeout(timeout);
|
|
55
|
+
resolve({
|
|
56
|
+
stdout,
|
|
57
|
+
stderr,
|
|
58
|
+
exitCode,
|
|
59
|
+
timedOut: false
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
child.on('error', err => {
|
|
63
|
+
clearTimeout(timeout);
|
|
64
|
+
reject(err);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
describe('CLI Integration', () => {
|
|
69
|
+
describe('--help flag', () => {
|
|
70
|
+
it('displays help text', async () => {
|
|
71
|
+
const result = await runCli(['--help']);
|
|
72
|
+
expect(result.stdout).toContain('Usage');
|
|
73
|
+
expect(result.stdout).toContain('npx @clerk/upgrade');
|
|
74
|
+
expect(result.stdout).toContain('--sdk');
|
|
75
|
+
expect(result.stdout).toContain('--dir');
|
|
76
|
+
expect(result.stdout).toContain('--dry-run');
|
|
77
|
+
expect(result.stdout).toContain('--skip-upgrade');
|
|
78
|
+
expect(result.stdout).toContain('--release');
|
|
79
|
+
expect(result.exitCode).toBe(0);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('--version flag', () => {
|
|
83
|
+
it('displays version', async () => {
|
|
84
|
+
const result = await runCli(['--version']);
|
|
85
|
+
expect(result.stdout).toMatch(/\d+\.\d+\.\d+/);
|
|
86
|
+
expect(result.exitCode).toBe(0);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('SDK Detection', () => {
|
|
90
|
+
it('detects nextjs SDK from project directory', async () => {
|
|
91
|
+
const dir = getFixturePath('nextjs-v6');
|
|
92
|
+
const result = await runCli(['--dir', dir, '--dry-run', '--skip-codemods'], {
|
|
93
|
+
timeout: 15000
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Combine stdout and stderr for full output
|
|
97
|
+
const output = result.stdout + result.stderr;
|
|
98
|
+
expect(output).toContain('@clerk/nextjs');
|
|
99
|
+
expect(output).toContain('dry run');
|
|
100
|
+
});
|
|
101
|
+
it('detects nextjs v7 as already upgraded', async () => {
|
|
102
|
+
const dir = getFixturePath('nextjs-v7');
|
|
103
|
+
const result = await runCli(['--dir', dir, '--dry-run', '--skip-codemods'], {
|
|
104
|
+
timeout: 15000
|
|
105
|
+
});
|
|
106
|
+
expect(result.stdout).toContain('@clerk/nextjs');
|
|
107
|
+
expect(result.stdout).toContain('already on the latest');
|
|
108
|
+
});
|
|
109
|
+
it('errors when SDK not detected and not provided in non-interactive mode', async () => {
|
|
110
|
+
const dir = getFixturePath('no-clerk');
|
|
111
|
+
const result = await runCli(['--dir', dir, '--dry-run', '--skip-codemods'], {
|
|
112
|
+
timeout: 5000
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Error messages go to stderr via console.error
|
|
116
|
+
const output = result.stdout + result.stderr;
|
|
117
|
+
expect(output).toContain('Could not detect Clerk SDK');
|
|
118
|
+
expect(output).toContain('--sdk');
|
|
119
|
+
expect(result.exitCode).toBe(1);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('--sdk flag', () => {
|
|
123
|
+
it('accepts explicit SDK specification', async () => {
|
|
124
|
+
const dir = getFixturePath('nextjs-v6');
|
|
125
|
+
const result = await runCli(['--dir', dir, '--sdk', 'nextjs', '--dry-run', '--skip-codemods'], {
|
|
126
|
+
timeout: 15000
|
|
127
|
+
});
|
|
128
|
+
expect(result.stdout).toContain('@clerk/nextjs');
|
|
129
|
+
});
|
|
130
|
+
it('accepts @clerk/ prefixed SDK name', async () => {
|
|
131
|
+
const dir = getFixturePath('nextjs-v6');
|
|
132
|
+
const result = await runCli(['--dir', dir, '--sdk', '@clerk/nextjs', '--dry-run', '--skip-codemods'], {
|
|
133
|
+
timeout: 15000
|
|
134
|
+
});
|
|
135
|
+
expect(result.stdout).toContain('@clerk/nextjs');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
describe('--dry-run flag', () => {
|
|
139
|
+
let fixture;
|
|
140
|
+
beforeEach(() => {
|
|
141
|
+
fixture = createTempFixture('nextjs-v6');
|
|
142
|
+
});
|
|
143
|
+
afterEach(() => {
|
|
144
|
+
fixture?.cleanup();
|
|
145
|
+
});
|
|
146
|
+
it('shows what would be done without making changes', async () => {
|
|
147
|
+
const result = await runCli(['--dir', fixture.path, '--dry-run', '--skip-codemods'], {
|
|
148
|
+
timeout: 15000
|
|
149
|
+
});
|
|
150
|
+
expect(result.stdout).toContain('[dry run]');
|
|
151
|
+
});
|
|
152
|
+
it('does not modify package.json in dry-run mode', async () => {
|
|
153
|
+
const fs = await import('node:fs');
|
|
154
|
+
const pkgBefore = fs.readFileSync(path.join(fixture.path, 'package.json'), 'utf8');
|
|
155
|
+
await runCli(['--dir', fixture.path, '--dry-run', '--skip-codemods'], {
|
|
156
|
+
timeout: 15000
|
|
157
|
+
});
|
|
158
|
+
const pkgAfter = fs.readFileSync(path.join(fixture.path, 'package.json'), 'utf8');
|
|
159
|
+
expect(pkgAfter).toBe(pkgBefore);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
describe('Version Display', () => {
|
|
163
|
+
it('shows current version in output', async () => {
|
|
164
|
+
const dir = getFixturePath('nextjs-v6');
|
|
165
|
+
const result = await runCli(['--dir', dir, '--dry-run', '--skip-codemods'], {
|
|
166
|
+
timeout: 15000
|
|
167
|
+
});
|
|
168
|
+
expect(result.stdout).toContain('v6');
|
|
169
|
+
});
|
|
170
|
+
it('shows upgrade path in output', async () => {
|
|
171
|
+
const dir = getFixturePath('nextjs-v6');
|
|
172
|
+
const result = await runCli(['--dir', dir, '--dry-run', '--skip-codemods'], {
|
|
173
|
+
timeout: 15000
|
|
174
|
+
});
|
|
175
|
+
expect(result.stdout).toContain('v6 → v7');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
describe('Package Manager Detection', () => {
|
|
179
|
+
it('detects pnpm from fixture', async () => {
|
|
180
|
+
const dir = getFixturePath('nextjs-v6');
|
|
181
|
+
const result = await runCli(['--dir', dir, '--dry-run', '--skip-codemods'], {
|
|
182
|
+
timeout: 15000
|
|
183
|
+
});
|
|
184
|
+
expect(result.stdout).toContain('pnpm');
|
|
185
|
+
});
|
|
186
|
+
it('detects yarn from fixture', async () => {
|
|
187
|
+
const dir = getFixturePath('react-v6');
|
|
188
|
+
const result = await runCli(['--dir', dir, '--dry-run', '--skip-codemods'], {
|
|
189
|
+
timeout: 15000
|
|
190
|
+
});
|
|
191
|
+
expect(result.stdout).toMatch(/yarn/i);
|
|
192
|
+
});
|
|
193
|
+
it('detects npm from fixture', async () => {
|
|
194
|
+
const dir = getFixturePath('expo-old-package');
|
|
195
|
+
const result = await runCli(['--dir', dir, '--dry-run', '--skip-codemods'], {
|
|
196
|
+
timeout: 15000
|
|
197
|
+
});
|
|
198
|
+
expect(result.stdout).toMatch(/npm/i);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
describe('Legacy Package Names', () => {
|
|
202
|
+
it('handles @clerk/clerk-react legacy package', async () => {
|
|
203
|
+
const dir = getFixturePath('react-v6');
|
|
204
|
+
const result = await runCli(['--dir', dir, '--dry-run', '--skip-codemods'], {
|
|
205
|
+
timeout: 15000
|
|
206
|
+
});
|
|
207
|
+
expect(result.stdout).toContain('@clerk/react');
|
|
208
|
+
});
|
|
209
|
+
it('handles @clerk/clerk-expo legacy package', async () => {
|
|
210
|
+
const dir = getFixturePath('expo-old-package');
|
|
211
|
+
const result = await runCli(['--dir', dir, '--dry-run', '--skip-codemods'], {
|
|
212
|
+
timeout: 15000
|
|
213
|
+
});
|
|
214
|
+
expect(result.stdout).toContain('@clerk/expo');
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
describe('Codemods', () => {
|
|
218
|
+
let fixture;
|
|
219
|
+
beforeEach(() => {
|
|
220
|
+
fixture = createTempFixture('nextjs-v6');
|
|
221
|
+
});
|
|
222
|
+
afterEach(() => {
|
|
223
|
+
fixture?.cleanup();
|
|
224
|
+
});
|
|
225
|
+
it('lists codemods that would run in dry-run mode', async () => {
|
|
226
|
+
const result = await runCli(['--dir', fixture.path, '--dry-run', '--skip-codemods'], {
|
|
227
|
+
timeout: 15000
|
|
228
|
+
});
|
|
229
|
+
expect(result.stdout).toContain('codemod');
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
describe('--skip-upgrade flag', () => {
|
|
233
|
+
let fixture;
|
|
234
|
+
beforeEach(() => {
|
|
235
|
+
fixture = createTempFixture('nextjs-v6');
|
|
236
|
+
});
|
|
237
|
+
afterEach(() => {
|
|
238
|
+
fixture?.cleanup();
|
|
239
|
+
});
|
|
240
|
+
it('skips the package upgrade step', async () => {
|
|
241
|
+
const result = await runCli(['--dir', fixture.path, '--skip-upgrade', '--skip-codemods'], {
|
|
242
|
+
timeout: 15000
|
|
243
|
+
});
|
|
244
|
+
expect(result.stdout).toContain('Skipping package upgrade');
|
|
245
|
+
expect(result.stdout).toContain('--skip-upgrade');
|
|
246
|
+
});
|
|
247
|
+
it('does not modify package.json when skipping upgrade', async () => {
|
|
248
|
+
const fs = await import('node:fs');
|
|
249
|
+
const pkgBefore = fs.readFileSync(path.join(fixture.path, 'package.json'), 'utf8');
|
|
250
|
+
await runCli(['--dir', fixture.path, '--skip-upgrade', '--skip-codemods'], {
|
|
251
|
+
timeout: 15000
|
|
252
|
+
});
|
|
253
|
+
const pkgAfter = fs.readFileSync(path.join(fixture.path, 'package.json'), 'utf8');
|
|
254
|
+
expect(pkgAfter).toBe(pkgBefore);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
describe('--release flag', () => {
|
|
258
|
+
it('loads specific release configuration', async () => {
|
|
259
|
+
const dir = getFixturePath('nextjs-v7');
|
|
260
|
+
const result = await runCli(['--dir', dir, '--release', 'core-3', '--dry-run', '--skip-codemods'], {
|
|
261
|
+
timeout: 15000
|
|
262
|
+
});
|
|
263
|
+
expect(result.stdout).toContain('core-3');
|
|
264
|
+
});
|
|
265
|
+
it('errors when release does not exist', async () => {
|
|
266
|
+
const dir = getFixturePath('nextjs-v6');
|
|
267
|
+
const result = await runCli(['--dir', dir, '--release', 'nonexistent-release', '--dry-run', '--skip-codemods'], {
|
|
268
|
+
timeout: 15000
|
|
269
|
+
});
|
|
270
|
+
const output = result.stdout + result.stderr;
|
|
271
|
+
expect(output).toContain('No upgrade path found');
|
|
272
|
+
expect(result.exitCode).toBe(1);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { getOldPackageName, getTargetPackageName, loadConfig } from '../../config.js';
|
|
3
|
+
describe('loadConfig', () => {
|
|
4
|
+
it('returns config with needsUpgrade: true for nextjs v6', async () => {
|
|
5
|
+
const config = await loadConfig('nextjs', 6);
|
|
6
|
+
expect(config).not.toBeNull();
|
|
7
|
+
expect(config.id).toBe('core-3');
|
|
8
|
+
expect(config.needsUpgrade).toBe(true);
|
|
9
|
+
expect(config.alreadyUpgraded).toBe(false);
|
|
10
|
+
});
|
|
11
|
+
it('returns config with alreadyUpgraded: true for nextjs v7', async () => {
|
|
12
|
+
const config = await loadConfig('nextjs', 7);
|
|
13
|
+
expect(config).not.toBeNull();
|
|
14
|
+
expect(config.id).toBe('core-3');
|
|
15
|
+
expect(config.needsUpgrade).toBe(false);
|
|
16
|
+
expect(config.alreadyUpgraded).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
it('returns config with needsUpgrade: true for react v6', async () => {
|
|
19
|
+
const config = await loadConfig('react', 6);
|
|
20
|
+
expect(config).not.toBeNull();
|
|
21
|
+
expect(config.needsUpgrade).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it('returns config with needsUpgrade: true for expo v2', async () => {
|
|
24
|
+
const config = await loadConfig('expo', 2);
|
|
25
|
+
expect(config).not.toBeNull();
|
|
26
|
+
expect(config.needsUpgrade).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
it('returns null for unsupported SDK version (too old)', async () => {
|
|
29
|
+
const config = await loadConfig('nextjs', 4);
|
|
30
|
+
expect(config).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
it('loads codemods array from config', async () => {
|
|
33
|
+
const config = await loadConfig('nextjs', 6);
|
|
34
|
+
expect(config.codemods).toBeDefined();
|
|
35
|
+
expect(Array.isArray(config.codemods)).toBe(true);
|
|
36
|
+
expect(config.codemods.length).toBeGreaterThan(0);
|
|
37
|
+
});
|
|
38
|
+
it('loads changes array from config', async () => {
|
|
39
|
+
const config = await loadConfig('nextjs', 6);
|
|
40
|
+
expect(config.changes).toBeDefined();
|
|
41
|
+
expect(Array.isArray(config.changes)).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
it('includes docsUrl in config', async () => {
|
|
44
|
+
const config = await loadConfig('nextjs', 6);
|
|
45
|
+
expect(config.docsUrl).toBeDefined();
|
|
46
|
+
expect(config.docsUrl).toContain('clerk.com');
|
|
47
|
+
});
|
|
48
|
+
describe('release parameter', () => {
|
|
49
|
+
it('loads specific release when provided', async () => {
|
|
50
|
+
const config = await loadConfig('nextjs', 7, 'core-3');
|
|
51
|
+
expect(config).not.toBeNull();
|
|
52
|
+
expect(config.id).toBe('core-3');
|
|
53
|
+
});
|
|
54
|
+
it('returns null for non-existent release', async () => {
|
|
55
|
+
const config = await loadConfig('nextjs', 6, 'nonexistent-release');
|
|
56
|
+
expect(config).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
it('ignores version status when release is specified', async () => {
|
|
59
|
+
const config = await loadConfig('nextjs', 7, 'core-3');
|
|
60
|
+
expect(config).not.toBeNull();
|
|
61
|
+
expect(config.alreadyUpgraded).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
it('loads changes for specific release', async () => {
|
|
64
|
+
const config = await loadConfig('nextjs', 6, 'core-3');
|
|
65
|
+
expect(config.changes).toBeDefined();
|
|
66
|
+
expect(Array.isArray(config.changes)).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('getTargetPackageName', () => {
|
|
71
|
+
it('returns @clerk/react for react sdk', () => {
|
|
72
|
+
expect(getTargetPackageName('react')).toBe('@clerk/react');
|
|
73
|
+
});
|
|
74
|
+
it('returns @clerk/react for clerk-react sdk', () => {
|
|
75
|
+
expect(getTargetPackageName('clerk-react')).toBe('@clerk/react');
|
|
76
|
+
});
|
|
77
|
+
it('returns @clerk/expo for expo sdk', () => {
|
|
78
|
+
expect(getTargetPackageName('expo')).toBe('@clerk/expo');
|
|
79
|
+
});
|
|
80
|
+
it('returns @clerk/expo for clerk-expo sdk', () => {
|
|
81
|
+
expect(getTargetPackageName('clerk-expo')).toBe('@clerk/expo');
|
|
82
|
+
});
|
|
83
|
+
it('returns @clerk/nextjs for nextjs sdk', () => {
|
|
84
|
+
expect(getTargetPackageName('nextjs')).toBe('@clerk/nextjs');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('getOldPackageName', () => {
|
|
88
|
+
it('returns @clerk/clerk-react for react sdk', () => {
|
|
89
|
+
expect(getOldPackageName('react')).toBe('@clerk/clerk-react');
|
|
90
|
+
});
|
|
91
|
+
it('returns @clerk/clerk-expo for expo sdk', () => {
|
|
92
|
+
expect(getOldPackageName('expo')).toBe('@clerk/clerk-expo');
|
|
93
|
+
});
|
|
94
|
+
it('returns null for nextjs sdk (no rename)', () => {
|
|
95
|
+
expect(getOldPackageName('nextjs')).toBeNull();
|
|
96
|
+
});
|
|
97
|
+
});
|