@clerk/upgrade 1.0.9 → 1.1.0-canary.v1997eac
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/dist/app.js +65 -43
- package/dist/cli.js +28 -24
- package/dist/codemods/__tests__/__fixtures__/transform-async-request.fixtures.js +216 -0
- package/dist/codemods/__tests__/transform-async-request.test.js +15 -0
- package/dist/codemods/index.js +25 -0
- package/dist/codemods/transform-async-request.cjs +121 -0
- package/dist/components/Codemod.js +66 -0
- package/dist/components/Header.js +11 -0
- package/dist/components/SDKWorkflow.js +114 -0
- package/dist/{scan.js → components/Scan.js} +16 -18
- package/dist/util/detect-package-manager.js +22 -0
- package/dist/util/expandable-list.js +1 -1
- package/dist/util/get-clerk-version.js +22 -2
- package/package.json +18 -14
package/dist/app.js
CHANGED
|
@@ -1,35 +1,46 @@
|
|
|
1
1
|
import { MultiSelect, Select, TextInput } from '@inkjs/ui';
|
|
2
|
-
import { Newline, Text } from 'ink';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
2
|
+
import { Newline, Text, useApp } from 'ink';
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
|
+
import { Header } from './components/Header.js';
|
|
5
|
+
import { Scan } from './components/Scan.js';
|
|
6
|
+
import { SDKWorkflow } from './components/SDKWorkflow.js';
|
|
6
7
|
import SDKS from './constants/sdks.js';
|
|
7
|
-
import
|
|
8
|
-
import getClerkMajorVersion from './util/get-clerk-version.js';
|
|
8
|
+
import { getClerkMajorVersion } from './util/get-clerk-version.js';
|
|
9
9
|
import guessFrameworks from './util/guess-framework.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Main CLI application component for handling Clerk SDK upgrades.
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} props - The `props` object.
|
|
15
|
+
* @param {string} [props.dir] - The directory to scan for files.
|
|
16
|
+
* @param {boolean} [props.disableTelemetry=false] - Flag to disable telemetry.
|
|
17
|
+
* @param {string} [props.fromVersion] - The current version of the SDK.
|
|
18
|
+
* @param {Array<string>} [props.ignore] - List of files or directories to ignore.
|
|
19
|
+
* @param {boolean} [props.noWarnings=false] - Flag to disable warnings.
|
|
20
|
+
* @param {string} [props.sdk] - The SDK to upgrade.
|
|
21
|
+
* @param {string} [props.toVersion] - The target version of the SDK.
|
|
22
|
+
* @param {boolean} [props.yolo=false] - Flag to enable YOLO mode.
|
|
23
|
+
*
|
|
24
|
+
* @returns {JSX.Element} The rendered component.
|
|
25
|
+
*/
|
|
26
|
+
export default function App(props) {
|
|
27
|
+
const {
|
|
28
|
+
noWarnings = false,
|
|
29
|
+
disableTelemetry = false
|
|
30
|
+
} = props;
|
|
31
|
+
const {
|
|
32
|
+
exit
|
|
33
|
+
} = useApp();
|
|
34
|
+
const [yolo, setYolo] = useState(props.yolo ?? false);
|
|
35
|
+
const [sdks, setSdks] = useState(props.sdk ? [props.sdk] : []);
|
|
22
36
|
const [sdkGuesses, setSdkGuesses] = useState([]);
|
|
23
37
|
const [sdkGuessConfirmed, setSdkGuessConfirmed] = useState(false);
|
|
24
38
|
const [sdkGuessAttempted, setSdkGuessAttempted] = useState(false);
|
|
25
|
-
|
|
26
|
-
// eslint-disable-next-line no-unused-vars
|
|
27
|
-
const [fromVersion, setFromVersion] = useState(_fromVersion);
|
|
39
|
+
const [fromVersion, setFromVersion] = useState(props.fromVersion);
|
|
28
40
|
const [fromVersionGuessAttempted, setFromVersionGuessAttempted] = useState(false);
|
|
29
|
-
|
|
30
|
-
const [
|
|
31
|
-
const [
|
|
32
|
-
const [ignore, setIgnore] = useState(_ignore);
|
|
41
|
+
const [toVersion, setToVersion] = useState(props.toVersion);
|
|
42
|
+
const [dir, setDir] = useState(props.dir);
|
|
43
|
+
const [ignore, setIgnore] = useState(props.ignore ?? []);
|
|
33
44
|
const [configComplete, setConfigComplete] = useState(false);
|
|
34
45
|
const [configVerified, setConfigVerified] = useState(false);
|
|
35
46
|
const [uuid, setUuid] = useState();
|
|
@@ -38,6 +49,24 @@ export default function App({
|
|
|
38
49
|
setSdks(SDKS.map(s => s.value));
|
|
39
50
|
setYolo(false);
|
|
40
51
|
}
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (toVersion === 'core-2') {
|
|
54
|
+
setFromVersion('core-1');
|
|
55
|
+
}
|
|
56
|
+
}, [toVersion]);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (fromVersion === 'core-1') {
|
|
59
|
+
setToVersion('core-2');
|
|
60
|
+
}
|
|
61
|
+
}, [fromVersion]);
|
|
62
|
+
|
|
63
|
+
// Handle the individual SDK upgrade
|
|
64
|
+
if (!fromVersion && !toVersion && sdks[0] === 'nextjs') {
|
|
65
|
+
return /*#__PURE__*/React.createElement(SDKWorkflow, {
|
|
66
|
+
packageManager: props.packageManager,
|
|
67
|
+
sdk: sdks[0]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
41
70
|
|
|
42
71
|
// We try to guess which SDK they are using
|
|
43
72
|
if (isEmpty(sdks) && isEmpty(sdkGuesses) && !sdkGuessAttempted) {
|
|
@@ -48,17 +77,13 @@ export default function App({
|
|
|
48
77
|
guesses,
|
|
49
78
|
_uuid
|
|
50
79
|
} = guessFrameworks(dir, disableTelemetry);
|
|
51
|
-
console.log({
|
|
52
|
-
guesses,
|
|
53
|
-
_uuid
|
|
54
|
-
});
|
|
55
80
|
setUuid(_uuid);
|
|
56
81
|
setSdkGuesses(guesses);
|
|
57
82
|
setSdkGuessAttempted(true);
|
|
58
83
|
}
|
|
59
84
|
|
|
60
85
|
// We try to guess which version of Clerk they are using
|
|
61
|
-
if (!fromVersion && !fromVersionGuess && !fromVersionGuessAttempted) {
|
|
86
|
+
if (isEmpty(sdks) && !fromVersion && !fromVersionGuess && !fromVersionGuessAttempted) {
|
|
62
87
|
fromVersionGuess = getClerkMajorVersion();
|
|
63
88
|
setFromVersionGuessAttempted(true);
|
|
64
89
|
}
|
|
@@ -76,14 +101,9 @@ export default function App({
|
|
|
76
101
|
color: "red"
|
|
77
102
|
}, "You are already on version ", toVersion, ", so there's no need to migrate!");
|
|
78
103
|
}
|
|
79
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(
|
|
80
|
-
name: "vice"
|
|
81
|
-
}, /*#__PURE__*/React.createElement(BigText, {
|
|
82
|
-
text: "Clerk Upgrade",
|
|
83
|
-
font: "tiny"
|
|
84
|
-
})), !configComplete && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, /*#__PURE__*/React.createElement(Text, {
|
|
104
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Header, null), !configComplete && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, /*#__PURE__*/React.createElement(Text, {
|
|
85
105
|
color: "blue"
|
|
86
|
-
}, "Hello friend!"), " We're excited to help you upgrade Clerk
|
|
106
|
+
}, "Hello friend!"), " We're excited to help you upgrade Clerk modules. Before we get started, a couple questions..."), /*#__PURE__*/React.createElement(Newline, null)), isEmpty(sdks) && !isEmpty(sdkGuesses) && !sdkGuessConfirmed && /*#__PURE__*/React.createElement(React.Fragment, null, sdkGuesses.length > 1 ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, "It looks like you are using the following Clerk SDKs in your project:"), sdkGuesses.map(guess => /*#__PURE__*/React.createElement(Text, {
|
|
87
107
|
key: guess.value
|
|
88
108
|
}, ' ', "- ", guess.label)), /*#__PURE__*/React.createElement(Text, null, "Is that right?")) : /*#__PURE__*/React.createElement(Text, null, "It looks like you are using the \"", sdkGuesses[0].label, "\" Clerk SDK in your project. Is that right?"), /*#__PURE__*/React.createElement(Select, {
|
|
89
109
|
options: [{
|
|
@@ -98,6 +118,8 @@ export default function App({
|
|
|
98
118
|
// if true, we were right so we set the sdk
|
|
99
119
|
if (item === 'yes') {
|
|
100
120
|
setSdks(sdkGuesses.map(guess => guess.value));
|
|
121
|
+
} else {
|
|
122
|
+
setSdkGuesses([]);
|
|
101
123
|
}
|
|
102
124
|
}
|
|
103
125
|
})), isEmpty(sdks) && isEmpty(sdkGuesses) && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, "Please select which Clerk SDK(s) you're using for your app:"), /*#__PURE__*/React.createElement(Text, {
|
|
@@ -106,7 +128,7 @@ export default function App({
|
|
|
106
128
|
options: SDKS,
|
|
107
129
|
onSubmit: value => setSdks(value),
|
|
108
130
|
visibleOptionCount: SDKS.length
|
|
109
|
-
})), !isEmpty(sdks)
|
|
131
|
+
})), !isEmpty(sdks) && fromVersion && toVersion && !dir && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, "Where would you like for us to scan for files in your project?"), /*#__PURE__*/React.createElement(Text, {
|
|
110
132
|
color: "gray"
|
|
111
133
|
}, "(globstar syntax supported)"), /*#__PURE__*/React.createElement(TextInput, {
|
|
112
134
|
defaultValue: "**/*",
|
|
@@ -133,14 +155,14 @@ export default function App({
|
|
|
133
155
|
}, " ", ignore.join(', ')))), /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.createElement(Text, null, "Does this look right?"), /*#__PURE__*/React.createElement(Select, {
|
|
134
156
|
options: [{
|
|
135
157
|
label: 'yes',
|
|
136
|
-
value:
|
|
158
|
+
value: 'yes'
|
|
137
159
|
}, {
|
|
138
|
-
label: 'no - exit, and
|
|
139
|
-
value:
|
|
160
|
+
label: 'no - exit, and I will try again',
|
|
161
|
+
value: 'no'
|
|
140
162
|
}],
|
|
141
163
|
onChange: value => {
|
|
142
|
-
if (!value) {
|
|
143
|
-
|
|
164
|
+
if (!value || value === 'no') {
|
|
165
|
+
exit();
|
|
144
166
|
} else {
|
|
145
167
|
setConfigVerified(true);
|
|
146
168
|
}
|
package/dist/cli.js
CHANGED
|
@@ -5,31 +5,31 @@ import React from 'react';
|
|
|
5
5
|
import App from './app.js';
|
|
6
6
|
import sdks from './constants/sdks.js';
|
|
7
7
|
const cli = meow(`
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Usage
|
|
9
|
+
$ clerk-upgrade
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
Options
|
|
12
|
+
--from Major version number you're upgrading from
|
|
13
|
+
--to Major version number you're upgrading to
|
|
14
|
+
--sdk Name of the SDK you're upgrading
|
|
15
|
+
--dir Directory you'd like to scan for files
|
|
16
|
+
--ignore Any files or directories you'd like to ignore
|
|
17
|
+
--packageManager The package manager you're using (npm, yarn, pnpm)
|
|
18
|
+
--noWarnings Do not print warnings, only items that must be fixed
|
|
19
|
+
--disableTelemetry Do not send anonymous usage telemetry
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
Examples
|
|
22
|
+
$ clerk-upgrade --sdk=nextjs --dir=src/**
|
|
22
23
|
$ clerk-upgrade --ignore=**/public/** --ignore=**/dist/**
|
|
24
|
+
$ clerk-upgrade --from=core-1 --to=core-2
|
|
23
25
|
`, {
|
|
24
26
|
importMeta: import.meta,
|
|
25
27
|
flags: {
|
|
26
28
|
from: {
|
|
27
|
-
type: 'string'
|
|
28
|
-
default: 'core-1'
|
|
29
|
+
type: 'string'
|
|
29
30
|
},
|
|
30
31
|
to: {
|
|
31
|
-
type: 'string'
|
|
32
|
-
default: 'core-2'
|
|
32
|
+
type: 'string'
|
|
33
33
|
},
|
|
34
34
|
sdk: {
|
|
35
35
|
type: 'string',
|
|
@@ -42,6 +42,9 @@ const cli = meow(`
|
|
|
42
42
|
type: 'string',
|
|
43
43
|
isMultiple: true
|
|
44
44
|
},
|
|
45
|
+
packageManager: {
|
|
46
|
+
type: 'string'
|
|
47
|
+
},
|
|
45
48
|
yolo: {
|
|
46
49
|
type: 'boolean'
|
|
47
50
|
},
|
|
@@ -53,15 +56,16 @@ const cli = meow(`
|
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
58
|
});
|
|
56
|
-
render(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
_ignore: cli.flags.ignore,
|
|
62
|
-
_yolo: cli.flags.yolo,
|
|
59
|
+
render(/*#__PURE__*/React.createElement(App, {
|
|
60
|
+
dir: cli.flags.dir,
|
|
61
|
+
disableTelemetry: cli.flags.disableTelemetry,
|
|
62
|
+
fromVersion: cli.flags.from,
|
|
63
|
+
ignore: cli.flags.ignore,
|
|
63
64
|
noWarnings: cli.flags.noWarnings,
|
|
64
|
-
|
|
65
|
+
packageManager: cli.flags.packageManager,
|
|
66
|
+
sdk: cli.flags.sdk,
|
|
67
|
+
toVersion: cli.flags.to,
|
|
68
|
+
yolo: cli.flags.yolo
|
|
65
69
|
})
|
|
66
70
|
// if having issues with errors being swallowed, uncomment this
|
|
67
71
|
// { debug: true },
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
export const fixtures = [{
|
|
2
|
+
name: 'Basic await transform',
|
|
3
|
+
source: `
|
|
4
|
+
import { auth } from '@clerk/nextjs/server';
|
|
5
|
+
|
|
6
|
+
export function any() {
|
|
7
|
+
const { userId } = auth();
|
|
8
|
+
return new Response(JSON.stringify({ userId }));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function another() {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
`,
|
|
15
|
+
output: `
|
|
16
|
+
import { auth } from '@clerk/nextjs/server';
|
|
17
|
+
|
|
18
|
+
export async function any() {
|
|
19
|
+
const { userId } = await auth();
|
|
20
|
+
return new Response(JSON.stringify({ userId }));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function another() {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
`
|
|
27
|
+
}, {
|
|
28
|
+
name: 'Arrow function await transform',
|
|
29
|
+
source: `
|
|
30
|
+
'use server';
|
|
31
|
+
|
|
32
|
+
import { patchCommunication, postSMSTemplate } from '@/app/api/instances';
|
|
33
|
+
import { Instance } from '@/app/api/types/instances';
|
|
34
|
+
import { Template } from '@/app/api/types/templates';
|
|
35
|
+
import { auth } from '@clerk/nextjs/server';
|
|
36
|
+
|
|
37
|
+
export const toggleSMSTemplate = async (
|
|
38
|
+
instanceId: string,
|
|
39
|
+
template: Template,
|
|
40
|
+
) => {
|
|
41
|
+
const { getToken } = auth();
|
|
42
|
+
|
|
43
|
+
return await postSMSTemplate(instanceId, template, {
|
|
44
|
+
token: await getToken(),
|
|
45
|
+
});
|
|
46
|
+
};`,
|
|
47
|
+
output: `
|
|
48
|
+
'use server';
|
|
49
|
+
|
|
50
|
+
import { patchCommunication, postSMSTemplate } from '@/app/api/instances';
|
|
51
|
+
import { Instance } from '@/app/api/types/instances';
|
|
52
|
+
import { Template } from '@/app/api/types/templates';
|
|
53
|
+
import { auth } from '@clerk/nextjs/server';
|
|
54
|
+
|
|
55
|
+
export const toggleSMSTemplate = async (
|
|
56
|
+
instanceId: string,
|
|
57
|
+
template: Template,
|
|
58
|
+
) => {
|
|
59
|
+
const { getToken } = await auth();
|
|
60
|
+
|
|
61
|
+
return await postSMSTemplate(instanceId, template, {
|
|
62
|
+
token: await getToken(),
|
|
63
|
+
});
|
|
64
|
+
};`
|
|
65
|
+
}, {
|
|
66
|
+
name: 'auth().protect -> await auth.protect()',
|
|
67
|
+
source: `
|
|
68
|
+
import { auth } from '@clerk/nextjs/server';
|
|
69
|
+
|
|
70
|
+
export function GET() {
|
|
71
|
+
const { userId } = auth().protect(
|
|
72
|
+
(has) => has({ role: 'admin' }) || has({ role: 'org:editor' }),
|
|
73
|
+
);
|
|
74
|
+
return new Response(JSON.stringify({ userId }));
|
|
75
|
+
}
|
|
76
|
+
`,
|
|
77
|
+
output: `
|
|
78
|
+
import { auth } from '@clerk/nextjs/server';
|
|
79
|
+
|
|
80
|
+
export async function GET() {
|
|
81
|
+
const { userId } = await auth.protect(
|
|
82
|
+
(has) => has({ role: 'admin' }) || has({ role: 'org:editor' }),
|
|
83
|
+
);
|
|
84
|
+
return new Response(JSON.stringify({ userId }));
|
|
85
|
+
}
|
|
86
|
+
`
|
|
87
|
+
}, {
|
|
88
|
+
name: 'Basic clerkMiddleware()',
|
|
89
|
+
source: `
|
|
90
|
+
import {
|
|
91
|
+
clerkMiddleware,
|
|
92
|
+
createRouteMatcher
|
|
93
|
+
} from "@clerk/nextjs/server"
|
|
94
|
+
|
|
95
|
+
const isPublicRoute = createRouteMatcher(["/", "/contact"])
|
|
96
|
+
|
|
97
|
+
export default clerkMiddleware((auth, req) => {
|
|
98
|
+
auth().protect(); // for any other route, require auth
|
|
99
|
+
})
|
|
100
|
+
`,
|
|
101
|
+
output: `
|
|
102
|
+
import {
|
|
103
|
+
clerkMiddleware,
|
|
104
|
+
createRouteMatcher
|
|
105
|
+
} from "@clerk/nextjs/server"
|
|
106
|
+
|
|
107
|
+
const isPublicRoute = createRouteMatcher(["/", "/contact"])
|
|
108
|
+
|
|
109
|
+
export default clerkMiddleware(async (auth, req) => {
|
|
110
|
+
await auth.protect(); // for any other route, require auth
|
|
111
|
+
})
|
|
112
|
+
`
|
|
113
|
+
}, {
|
|
114
|
+
name: 'Complex clerkMiddleware()',
|
|
115
|
+
source: `
|
|
116
|
+
import {
|
|
117
|
+
clerkMiddleware,
|
|
118
|
+
createRouteMatcher
|
|
119
|
+
} from "@clerk/nextjs/server"
|
|
120
|
+
import createMiddleware from "next-intl/middleware"
|
|
121
|
+
|
|
122
|
+
const intlMiddleware = createMiddleware({
|
|
123
|
+
locales: ["en", "de"],
|
|
124
|
+
defaultLocale: "en",
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const isDashboardRoute = createRouteMatcher(["/dashboard(.*)"])
|
|
128
|
+
|
|
129
|
+
export default clerkMiddleware((auth, request) => {
|
|
130
|
+
if (isDashboardRoute(request)) auth().protect()
|
|
131
|
+
|
|
132
|
+
return intlMiddleware(request)
|
|
133
|
+
})
|
|
134
|
+
`,
|
|
135
|
+
output: `
|
|
136
|
+
import {
|
|
137
|
+
clerkMiddleware,
|
|
138
|
+
createRouteMatcher
|
|
139
|
+
} from "@clerk/nextjs/server"
|
|
140
|
+
import createMiddleware from "next-intl/middleware"
|
|
141
|
+
|
|
142
|
+
const intlMiddleware = createMiddleware({
|
|
143
|
+
locales: ["en", "de"],
|
|
144
|
+
defaultLocale: "en",
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const isDashboardRoute = createRouteMatcher(["/dashboard(.*)"])
|
|
148
|
+
|
|
149
|
+
export default clerkMiddleware(async (auth, request) => {
|
|
150
|
+
if (isDashboardRoute(request)) await auth.protect()
|
|
151
|
+
|
|
152
|
+
return intlMiddleware(request)
|
|
153
|
+
})
|
|
154
|
+
`
|
|
155
|
+
}, {
|
|
156
|
+
name: 'Complex clerkMiddleware() with protect being destructured from auth()',
|
|
157
|
+
source: `
|
|
158
|
+
import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
159
|
+
|
|
160
|
+
export default clerkMiddleware(
|
|
161
|
+
(auth, req) => {
|
|
162
|
+
const { protect, sessionClaims } = auth();
|
|
163
|
+
|
|
164
|
+
protect();
|
|
165
|
+
},
|
|
166
|
+
);
|
|
167
|
+
`,
|
|
168
|
+
output: `
|
|
169
|
+
import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
170
|
+
|
|
171
|
+
export default clerkMiddleware(
|
|
172
|
+
async (auth, req) => {
|
|
173
|
+
const {
|
|
174
|
+
sessionClaims
|
|
175
|
+
} = await auth();
|
|
176
|
+
|
|
177
|
+
await auth.protect();
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
`
|
|
181
|
+
}, {
|
|
182
|
+
name: 'Complex clerkMiddleware() without protect()',
|
|
183
|
+
source: `
|
|
184
|
+
import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
185
|
+
|
|
186
|
+
export default clerkMiddleware(
|
|
187
|
+
(auth, req) => {
|
|
188
|
+
const { redirectToSignIn } = auth();
|
|
189
|
+
|
|
190
|
+
redirectToSignIn();
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
`,
|
|
194
|
+
output: `
|
|
195
|
+
import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
196
|
+
|
|
197
|
+
export default clerkMiddleware(
|
|
198
|
+
async (auth, req) => {
|
|
199
|
+
const { redirectToSignIn } = await auth();
|
|
200
|
+
|
|
201
|
+
redirectToSignIn();
|
|
202
|
+
},
|
|
203
|
+
);
|
|
204
|
+
`
|
|
205
|
+
}, {
|
|
206
|
+
name: 'Does not transform other imports',
|
|
207
|
+
source: `
|
|
208
|
+
import { auth } from '@some/other/module';
|
|
209
|
+
|
|
210
|
+
export function any() {
|
|
211
|
+
const { IBauthed } = auth();
|
|
212
|
+
return new Response(JSON.stringify({ IBauthed }));
|
|
213
|
+
}
|
|
214
|
+
`,
|
|
215
|
+
output: ''
|
|
216
|
+
}];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { applyTransform } from 'jscodeshift/dist/testUtils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import transformer from '../transform-async-request.cjs';
|
|
4
|
+
import { fixtures } from './__fixtures__/transform-async-request.fixtures';
|
|
5
|
+
describe('transform-async-request', () => {
|
|
6
|
+
it.each(fixtures)(`$name`, ({
|
|
7
|
+
source,
|
|
8
|
+
output
|
|
9
|
+
}) => {
|
|
10
|
+
const result = applyTransform(transformer, {}, {
|
|
11
|
+
source
|
|
12
|
+
});
|
|
13
|
+
expect(result).toEqual(output.trim());
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { globby } from 'globby';
|
|
4
|
+
import { run } from 'jscodeshift/src/Runner.js';
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
export async function runCodemod(transform, glob, options) {
|
|
7
|
+
if (!transform) {
|
|
8
|
+
throw new Error('No transform provided');
|
|
9
|
+
}
|
|
10
|
+
const resolvedPath = resolve(__dirname, `${transform}.cjs`);
|
|
11
|
+
const paths = await globby(glob, {
|
|
12
|
+
ignore: ['**/*.md', 'node_modules/**', '**/node_modules/**', '.git/**', '**/*.json', 'package.json', '**/package.json', 'package-lock.json', '**/package-lock.json', 'yarn.lock', '**/yarn.lock', 'pnpm-lock.yaml', '**/pnpm-lock.yaml', 'yalc.lock', '**/*.(ico|png|webp|svg|gif|jpg|jpeg)+',
|
|
13
|
+
// common image files
|
|
14
|
+
'**/*.(mp4|mkv|wmv|m4v|mov|avi|flv|webm|flac|mka|m4a|aac|ogg)+',
|
|
15
|
+
// common video files] }).then(files => {
|
|
16
|
+
'**/*.(css|scss|sass|less|styl)+' // common style files
|
|
17
|
+
]
|
|
18
|
+
});
|
|
19
|
+
return await run(resolvedPath, paths ?? [], {
|
|
20
|
+
dry: false,
|
|
21
|
+
...options,
|
|
22
|
+
// we must silence stdout to prevent output from interfering with ink CLI
|
|
23
|
+
silent: true
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transforms the source code by modifying calls to `auth().protect()` and ensuring
|
|
3
|
+
* that calls to `auth` are awaited within function declarations.
|
|
4
|
+
*
|
|
5
|
+
* @param {import('jscodeshift').FileInfo} FileInfo - The parameters object
|
|
6
|
+
* @param {import('jscodeshift').API} api - The API object provided by jscodeshift
|
|
7
|
+
* @param {Object} _options - Additional options (unused)
|
|
8
|
+
* @returns {string|undefined} - The transformed source code if modifications were made, otherwise undefined
|
|
9
|
+
*/
|
|
10
|
+
module.exports = function transformAsyncRequest({
|
|
11
|
+
path,
|
|
12
|
+
source
|
|
13
|
+
}, {
|
|
14
|
+
jscodeshift: j
|
|
15
|
+
}, _options) {
|
|
16
|
+
const root = j(source);
|
|
17
|
+
let dirtyFlag = false;
|
|
18
|
+
|
|
19
|
+
// Short-circuit if the import from '@clerk/nextjs/server' is not found
|
|
20
|
+
if (root.find(j.ImportDeclaration, {
|
|
21
|
+
source: {
|
|
22
|
+
value: '@clerk/nextjs/server'
|
|
23
|
+
}
|
|
24
|
+
}).size() === 0) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Helper function to make a function async
|
|
29
|
+
function makeFunctionAsync(func) {
|
|
30
|
+
if (!func.async) {
|
|
31
|
+
func.async = true;
|
|
32
|
+
dirtyFlag = true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function transformFunctions(func) {
|
|
36
|
+
let authCallFound = false;
|
|
37
|
+
let authProtectFound = false;
|
|
38
|
+
|
|
39
|
+
// Find the auth().protect call and transform it
|
|
40
|
+
j(func).find(j.CallExpression, {
|
|
41
|
+
callee: {
|
|
42
|
+
type: 'MemberExpression',
|
|
43
|
+
object: {
|
|
44
|
+
type: 'CallExpression',
|
|
45
|
+
callee: {
|
|
46
|
+
name: 'auth'
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
property: {
|
|
50
|
+
name: 'protect'
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}).forEach(authProtectPath => {
|
|
54
|
+
// Replace auth().protect with await auth.protect
|
|
55
|
+
authProtectPath.node.callee = j.memberExpression(j.identifier('auth'), j.identifier('protect'));
|
|
56
|
+
authProtectPath.replace(j.awaitExpression(authProtectPath.node));
|
|
57
|
+
authProtectFound = true;
|
|
58
|
+
dirtyFlag = true;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Find the auth() call and transform it
|
|
62
|
+
j(func).find(j.CallExpression, {
|
|
63
|
+
callee: {
|
|
64
|
+
name: 'auth'
|
|
65
|
+
}
|
|
66
|
+
}).forEach(authPath => {
|
|
67
|
+
authCallFound = true;
|
|
68
|
+
// Ensure the call to 'auth' is awaited
|
|
69
|
+
if (!j.AwaitExpression.check(authPath.parent.node)) {
|
|
70
|
+
j(authPath).replaceWith(j.awaitExpression(authPath.node));
|
|
71
|
+
dirtyFlag = true;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Add 'async' keyword to the function declaration
|
|
76
|
+
if (!func.node.async && (authCallFound || authProtectFound)) {
|
|
77
|
+
func.node.async = true;
|
|
78
|
+
dirtyFlag = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Find all function expressions
|
|
83
|
+
root.find(j.FunctionDeclaration).forEach(transformFunctions);
|
|
84
|
+
root.find(j.ArrowFunctionExpression).forEach(transformFunctions);
|
|
85
|
+
|
|
86
|
+
// Find the default export which is a call to clerkMiddleware
|
|
87
|
+
root.find(j.ExportDefaultDeclaration).forEach(path => {
|
|
88
|
+
const declaration = path.node.declaration;
|
|
89
|
+
if (j.CallExpression.check(declaration) && j.Identifier.check(declaration.callee) && declaration.callee.name === 'clerkMiddleware') {
|
|
90
|
+
const middlewareFunction = declaration.arguments[0];
|
|
91
|
+
if (j.FunctionExpression.check(middlewareFunction) || j.ArrowFunctionExpression.check(middlewareFunction)) {
|
|
92
|
+
// Add async keyword to the function
|
|
93
|
+
makeFunctionAsync(middlewareFunction);
|
|
94
|
+
|
|
95
|
+
// Find the destructuring assignment and modify it
|
|
96
|
+
j(middlewareFunction.body).find(j.VariableDeclarator).forEach(varPath => {
|
|
97
|
+
const id = varPath.node.id;
|
|
98
|
+
const init = varPath.node.init;
|
|
99
|
+
if ((j.CallExpression.check(init) || j.AwaitExpression.check(init)) && (init.callee?.name === 'auth' || init.argument?.callee?.name === 'auth')) {
|
|
100
|
+
// Remove 'protect' from destructuring
|
|
101
|
+
id.properties = id.properties.filter(prop => {
|
|
102
|
+
return !(j.Identifier.check(prop.key) && prop.key.name === 'protect');
|
|
103
|
+
});
|
|
104
|
+
dirtyFlag = true;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Replace protect() call with await auth.protect()
|
|
109
|
+
j(middlewareFunction.body).find(j.ExpressionStatement).forEach(exprPath => {
|
|
110
|
+
const expr = exprPath.node.expression;
|
|
111
|
+
if (j.CallExpression.check(expr) && j.Identifier.check(expr.callee) && expr.callee.name === 'protect') {
|
|
112
|
+
exprPath.replace(j.expressionStatement(j.awaitExpression(j.callExpression(j.memberExpression(j.identifier('auth'), j.identifier('protect')), []))));
|
|
113
|
+
dirtyFlag = true;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return dirtyFlag ? root.toSource() : undefined;
|
|
120
|
+
};
|
|
121
|
+
module.exports.parser = 'tsx';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Spinner, StatusMessage, TextInput } from '@inkjs/ui';
|
|
2
|
+
import { Newline, Text } from 'ink';
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
|
+
import { runCodemod } from '../codemods/index.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Codemod component that allows users to run a codemod transformation on their project files.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {Function} props.callback - The callback function to be called after the codemod is run.
|
|
11
|
+
* @param {string} props.glob - The directory to scan for files in the project.
|
|
12
|
+
* @param {string} props.sdk - The SDK name to be used in the codemod.
|
|
13
|
+
* @param {string} props.transform - The transformation to be applied by the codemod.
|
|
14
|
+
*
|
|
15
|
+
* @returns {JSX.Element} The rendered Codemod component.
|
|
16
|
+
*/
|
|
17
|
+
export function Codemod(props) {
|
|
18
|
+
const {
|
|
19
|
+
callback,
|
|
20
|
+
sdk,
|
|
21
|
+
transform
|
|
22
|
+
} = props;
|
|
23
|
+
const [error, setError] = useState();
|
|
24
|
+
const [glob, setGlob] = useState(props.glob);
|
|
25
|
+
const [result, setResult] = useState();
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!glob) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
runCodemod(transform, glob).then(res => {
|
|
31
|
+
setResult(res);
|
|
32
|
+
}).catch(err => {
|
|
33
|
+
setError(err);
|
|
34
|
+
}).finally(() => {
|
|
35
|
+
callback(true);
|
|
36
|
+
});
|
|
37
|
+
}, [glob]);
|
|
38
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, glob ? /*#__PURE__*/React.createElement(StatusMessage, {
|
|
39
|
+
variant: "success"
|
|
40
|
+
}, "Scanning for files in your project... ", glob.toString()) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, "Where would you like for us to scan for files in your project?"), /*#__PURE__*/React.createElement(Text, {
|
|
41
|
+
color: "gray"
|
|
42
|
+
}, "(globstar syntax supported)"), glob ? /*#__PURE__*/React.createElement(Text, null, glob.toString()) : /*#__PURE__*/React.createElement(TextInput, {
|
|
43
|
+
defaultValue: "**/*",
|
|
44
|
+
onSubmit: val => {
|
|
45
|
+
setGlob(val.split(/[ ,]/));
|
|
46
|
+
}
|
|
47
|
+
})), !result && !error && glob && /*#__PURE__*/React.createElement(Spinner, {
|
|
48
|
+
label: `Running @clerk/${sdk}</Text> codemod... ${transform}`
|
|
49
|
+
}), result && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StatusMessage, {
|
|
50
|
+
variant: "success"
|
|
51
|
+
}, "Running ", /*#__PURE__*/React.createElement(Text, {
|
|
52
|
+
bold: true
|
|
53
|
+
}, "@clerk/", sdk), " codemod... ", transform, " complete!"), /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.createElement(Text, {
|
|
54
|
+
bold: true
|
|
55
|
+
}, "Codemod results:"), /*#__PURE__*/React.createElement(Text, {
|
|
56
|
+
color: "red"
|
|
57
|
+
}, result.error ?? 0, " errors"), /*#__PURE__*/React.createElement(Text, {
|
|
58
|
+
color: "green"
|
|
59
|
+
}, result.ok ?? 0, " ok"), /*#__PURE__*/React.createElement(Text, {
|
|
60
|
+
color: "yellow"
|
|
61
|
+
}, result.skip ?? 0, " skipped"), /*#__PURE__*/React.createElement(Text, {
|
|
62
|
+
color: "gray"
|
|
63
|
+
}, result.nochange ?? 0, " unmodified"), result.timeElapsed && /*#__PURE__*/React.createElement(Text, null, "Time elapsed: ", result.timeElapsed), /*#__PURE__*/React.createElement(Newline, null)), error && /*#__PURE__*/React.createElement(Text, {
|
|
64
|
+
color: "red"
|
|
65
|
+
}, error.message));
|
|
66
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import BigText from 'ink-big-text';
|
|
2
|
+
import Gradient from 'ink-gradient';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
export function Header() {
|
|
5
|
+
return /*#__PURE__*/React.createElement(Gradient, {
|
|
6
|
+
name: "vice"
|
|
7
|
+
}, /*#__PURE__*/React.createElement(BigText, {
|
|
8
|
+
text: "Clerk Upgrade",
|
|
9
|
+
font: "tiny"
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Select, Spinner, StatusMessage } from '@inkjs/ui';
|
|
2
|
+
import { execa } from 'execa';
|
|
3
|
+
import { Text } from 'ink';
|
|
4
|
+
import React, { useEffect, useState } from 'react';
|
|
5
|
+
import { getUpgradeCommand } from '../util/detect-package-manager.js';
|
|
6
|
+
import { getClerkSdkVersion } from '../util/get-clerk-version.js';
|
|
7
|
+
import { Codemod } from './Codemod.js';
|
|
8
|
+
import { Header } from './Header.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* SDKWorkflow component handles the upgrade process for a given SDK.
|
|
12
|
+
* It checks the current version of the SDK and provides the necessary steps
|
|
13
|
+
* to upgrade or run codemods based on the version.
|
|
14
|
+
*
|
|
15
|
+
* @component
|
|
16
|
+
* @param {Object} props
|
|
17
|
+
* @param {string} props.packageManager - The package manager to use for the upgrade, if needed.
|
|
18
|
+
* @param {string} props.sdk - The SDK to be upgraded.
|
|
19
|
+
*
|
|
20
|
+
* @returns {JSX.Element} The rendered component.
|
|
21
|
+
*/
|
|
22
|
+
export function SDKWorkflow(props) {
|
|
23
|
+
const {
|
|
24
|
+
packageManager,
|
|
25
|
+
sdk
|
|
26
|
+
} = props;
|
|
27
|
+
const [done, setDone] = useState(false);
|
|
28
|
+
const [upgradeComplete, setUpgradeComplete] = useState(false);
|
|
29
|
+
const version = getClerkSdkVersion(sdk);
|
|
30
|
+
if (sdk !== 'nextjs') {
|
|
31
|
+
return /*#__PURE__*/React.createElement(StatusMessage, {
|
|
32
|
+
variant: "error"
|
|
33
|
+
}, "The SDK upgrade functionality is only available for ", /*#__PURE__*/React.createElement(Text, {
|
|
34
|
+
bold: true
|
|
35
|
+
}, "@clerk/nextjs"), " at the moment.");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Right now, we only have one codemod for the async request transformation
|
|
39
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Header, null), version === 5 && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(UpgradeCommand, {
|
|
40
|
+
callback: setUpgradeComplete,
|
|
41
|
+
packageManager: packageManager,
|
|
42
|
+
sdk: sdk
|
|
43
|
+
}), upgradeComplete ? /*#__PURE__*/React.createElement(Codemod, {
|
|
44
|
+
callback: setDone,
|
|
45
|
+
sdk: sdk,
|
|
46
|
+
transform: "transform-async-request"
|
|
47
|
+
}) : null), !done && version === 6 && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, "Looks like you are already on the latest version of ", /*#__PURE__*/React.createElement(Text, {
|
|
48
|
+
bold: true
|
|
49
|
+
}, "@clerk/", sdk), ". Would you like to run the associated codemod?."), upgradeComplete ? /*#__PURE__*/React.createElement(Codemod, {
|
|
50
|
+
sdk: sdk,
|
|
51
|
+
callback: setDone
|
|
52
|
+
}) : /*#__PURE__*/React.createElement(Select, {
|
|
53
|
+
options: [{
|
|
54
|
+
label: 'yes',
|
|
55
|
+
value: 'yes'
|
|
56
|
+
}, {
|
|
57
|
+
label: 'no',
|
|
58
|
+
value: 'no'
|
|
59
|
+
}],
|
|
60
|
+
onChange: value => {
|
|
61
|
+
if (value === 'yes') {
|
|
62
|
+
setUpgradeComplete(true);
|
|
63
|
+
} else {
|
|
64
|
+
setDone(true);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})), done && /*#__PURE__*/React.createElement(StatusMessage, {
|
|
68
|
+
variant: "success"
|
|
69
|
+
}, "Done upgrading ", /*#__PURE__*/React.createElement(Text, {
|
|
70
|
+
bold: true
|
|
71
|
+
}, "@clerk/nextjs")));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Component that runs an upgrade command for a given SDK and handles the result.
|
|
76
|
+
*
|
|
77
|
+
* @component
|
|
78
|
+
* @param {Object} props
|
|
79
|
+
* @param {Function} props.callback - The callback function to be called after the command execution.
|
|
80
|
+
* @param {string} props.sdk - The SDK for which the upgrade command is run.
|
|
81
|
+
* @returns {JSX.Element} The rendered component.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* <UpgradeCommand sdk="example-sdk" callback={handleUpgrade} />
|
|
85
|
+
*/
|
|
86
|
+
function UpgradeCommand({
|
|
87
|
+
callback,
|
|
88
|
+
sdk
|
|
89
|
+
}) {
|
|
90
|
+
const [error, setError] = useState();
|
|
91
|
+
const [result, setResult] = useState();
|
|
92
|
+
const command = getUpgradeCommand(sdk);
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
execa({
|
|
95
|
+
shell: true
|
|
96
|
+
})`${command}`.then(res => {
|
|
97
|
+
setResult(res);
|
|
98
|
+
callback(true);
|
|
99
|
+
}).catch(err => {
|
|
100
|
+
setError(err);
|
|
101
|
+
});
|
|
102
|
+
}, [command]);
|
|
103
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, !result && !error && /*#__PURE__*/React.createElement(Spinner, {
|
|
104
|
+
label: `Running upgrade command: ${command}`
|
|
105
|
+
}), result && /*#__PURE__*/React.createElement(StatusMessage, {
|
|
106
|
+
variant: "success"
|
|
107
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
108
|
+
bold: true
|
|
109
|
+
}, "@clerk/", sdk), " upgraded successfully to ", /*#__PURE__*/React.createElement(Text, {
|
|
110
|
+
bold: true
|
|
111
|
+
}, "latest!")), error && /*#__PURE__*/React.createElement(StatusMessage, {
|
|
112
|
+
variant: "error"
|
|
113
|
+
}, "Upgrade failed!"));
|
|
114
|
+
}
|
|
@@ -5,17 +5,18 @@ import indexToPosition from 'index-to-position';
|
|
|
5
5
|
import { Newline, Text } from 'ink';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import React, { useEffect, useState } from 'react';
|
|
8
|
-
import ExpandableList from '
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
import ExpandableList from '../util/expandable-list.js';
|
|
9
|
+
export function Scan(props) {
|
|
10
|
+
const {
|
|
11
|
+
fromVersion,
|
|
12
|
+
toVersion,
|
|
13
|
+
sdks,
|
|
14
|
+
dir,
|
|
15
|
+
ignore,
|
|
16
|
+
noWarnings,
|
|
17
|
+
uuid,
|
|
18
|
+
disableTelemetry
|
|
19
|
+
} = props;
|
|
19
20
|
// NOTE: if the difference between fromVersion and toVersion is greater than 1
|
|
20
21
|
// we need to do a little extra work here and import two matchers,
|
|
21
22
|
// sequence them after each other, and clearly mark which version migration
|
|
@@ -36,7 +37,7 @@ export default function Scan({
|
|
|
36
37
|
// { sdkName: [{ title: 'x', matcher: /x/, slug: 'x', ... }] }
|
|
37
38
|
useEffect(() => {
|
|
38
39
|
setStatus(`Loading data for ${toVersion} migration`);
|
|
39
|
-
import(
|
|
40
|
+
import(`../versions/${toVersion}/index.js`).then(version => {
|
|
40
41
|
setMatchers(sdks.reduce((m, sdk) => {
|
|
41
42
|
m[sdk] = version.default[sdk];
|
|
42
43
|
return m;
|
|
@@ -49,12 +50,9 @@ export default function Scan({
|
|
|
49
50
|
// result = `files` set to format: ['/filename', '/other/filename']
|
|
50
51
|
useEffect(() => {
|
|
51
52
|
setStatus('Collecting files to scan');
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
);
|
|
56
|
-
globby(convertPathToPattern(path.resolve(dir)), {
|
|
57
|
-
ignore: ignore.filter(Boolean)
|
|
53
|
+
const pattern = convertPathToPattern(path.resolve(dir));
|
|
54
|
+
globby(pattern, {
|
|
55
|
+
ignore: ['node_modules/**', '**/node_modules/**', '.git/**', 'package.json', '**/package.json', 'package-lock.json', '**/package-lock.json', 'yarn.lock', '**/yarn.lock', 'pnpm-lock.yaml', '**/pnpm-lock.yaml', '**/*.(png|webp|svg|gif|jpg|jpeg)+', '**/*.(mp4|mkv|wmv|m4v|mov|avi|flv|webm|flac|mka|m4a|aac|ogg)+', ...ignore].filter(Boolean)
|
|
58
56
|
}).then(files => {
|
|
59
57
|
setFiles(files);
|
|
60
58
|
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
export function detectPackageManager() {
|
|
3
|
+
if (existsSync('package-lock.json')) {
|
|
4
|
+
return 'npm';
|
|
5
|
+
} else if (existsSync('yarn.lock')) {
|
|
6
|
+
return 'yarn';
|
|
7
|
+
} else if (existsSync('pnpm-lock.yaml')) {
|
|
8
|
+
return 'pnpm';
|
|
9
|
+
} else {
|
|
10
|
+
return 'npm';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function getUpgradeCommand(sdk, packageManager) {
|
|
14
|
+
switch (packageManager || detectPackageManager()) {
|
|
15
|
+
case 'yarn':
|
|
16
|
+
return `yarn add @clerk/${sdk}@latest`;
|
|
17
|
+
case 'pnpm':
|
|
18
|
+
return `pnpm add @clerk/${sdk}@latest`;
|
|
19
|
+
default:
|
|
20
|
+
return `npm install @clerk/${sdk}@latest`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -74,7 +74,7 @@ export default function ExpandableList({
|
|
|
74
74
|
bottomRight: ' ',
|
|
75
75
|
right: ''
|
|
76
76
|
};
|
|
77
|
-
memo.push(
|
|
77
|
+
memo.push(/*#__PURE__*/React.createElement(Box, {
|
|
78
78
|
borderStyle: item.focused ? doubleBorderStyle : singleBorderStyle,
|
|
79
79
|
flexDirection: "column",
|
|
80
80
|
borderColor: item.focused ? 'blue' : 'white',
|
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
import { readPackageSync } from 'read-pkg';
|
|
2
2
|
import semverRegex from 'semver-regex';
|
|
3
|
-
export
|
|
3
|
+
export function getClerkMajorVersion() {
|
|
4
4
|
const pkg = readPackageSync();
|
|
5
5
|
const clerk = pkg.dependencies.clerk;
|
|
6
|
-
return clerk ? semverRegex.exec(clerk)[0][0] : false;
|
|
6
|
+
return clerk ? semverRegex().exec(clerk)[0][0] : false;
|
|
7
|
+
}
|
|
8
|
+
export function getClerkSdkVersion(sdk) {
|
|
9
|
+
const pkg = readPackageSync();
|
|
10
|
+
const clerkSdk = pkg.dependencies[`@clerk/${sdk}`];
|
|
11
|
+
if (!clerkSdk) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return getMajorVersion(clerkSdk.replace('^', '').replace('~', ''));
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function getMajorVersion(semver) {
|
|
21
|
+
const match = semver.match(semverRegex());
|
|
22
|
+
if (match) {
|
|
23
|
+
const [major] = match[0].split('.');
|
|
24
|
+
return parseInt(major, 10); // Return as an integer
|
|
25
|
+
}
|
|
26
|
+
throw new Error('Invalid semver string');
|
|
7
27
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clerk/upgrade",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.1.0-canary.v1997eac",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/clerk/javascript.git",
|
|
7
|
+
"directory": "packages/upgrade"
|
|
8
|
+
},
|
|
4
9
|
"license": "MIT",
|
|
5
10
|
"type": "module",
|
|
6
11
|
"main": "dist/cli.js",
|
|
@@ -11,16 +16,12 @@
|
|
|
11
16
|
"dist"
|
|
12
17
|
],
|
|
13
18
|
"scripts": {
|
|
14
|
-
"build": "npm run clean && NODE_ENV=production babel --out-dir=dist src --copy-files",
|
|
19
|
+
"build": "npm run clean && NODE_ENV=production babel --keep-file-extension --out-dir=dist src --copy-files",
|
|
15
20
|
"clean": "del-cli dist/*",
|
|
16
|
-
"dev": "babel --out-dir=dist --watch src --copy-files",
|
|
21
|
+
"dev": "babel --keep-file-extension --out-dir=dist --watch src --copy-files",
|
|
17
22
|
"lint": "eslint src/",
|
|
18
|
-
"lint:publint": "publint"
|
|
19
|
-
|
|
20
|
-
"repository": {
|
|
21
|
-
"type": "git",
|
|
22
|
-
"url": "git+https://github.com/clerk/javascript.git",
|
|
23
|
-
"directory": "packages/upgrade"
|
|
23
|
+
"lint:publint": "publint",
|
|
24
|
+
"test": "vitest"
|
|
24
25
|
},
|
|
25
26
|
"babel": {
|
|
26
27
|
"presets": [
|
|
@@ -28,16 +29,18 @@
|
|
|
28
29
|
]
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|
|
31
|
-
"@inkjs/ui": "^
|
|
32
|
+
"@inkjs/ui": "^2.0.0",
|
|
32
33
|
"@jescalan/ink-markdown": "^2.0.0",
|
|
33
34
|
"ejs": "3.1.10",
|
|
35
|
+
"execa": "9.4.1",
|
|
34
36
|
"globby": "^14.0.1",
|
|
35
37
|
"gray-matter": "^4.0.3",
|
|
36
38
|
"index-to-position": "^0.1.2",
|
|
37
|
-
"ink": "^
|
|
39
|
+
"ink": "^5.0.1",
|
|
38
40
|
"ink-big-text": "^2.0.0",
|
|
39
41
|
"ink-gradient": "^3.0.0",
|
|
40
|
-
"ink-link": "^
|
|
42
|
+
"ink-link": "^4.1.0",
|
|
43
|
+
"jscodeshift": "^17.0.0",
|
|
41
44
|
"marked": "^11.1.1",
|
|
42
45
|
"meow": "^11.0.0",
|
|
43
46
|
"react": "^18.3.1",
|
|
@@ -48,9 +51,10 @@
|
|
|
48
51
|
"devDependencies": {
|
|
49
52
|
"@babel/cli": "^7.24.7",
|
|
50
53
|
"@babel/preset-react": "^7.24.7",
|
|
51
|
-
"
|
|
54
|
+
"@types/jscodeshift": "^0.12.0",
|
|
52
55
|
"del-cli": "^5.1.0",
|
|
53
|
-
"eslint-config-custom": "*"
|
|
56
|
+
"eslint-config-custom": "*",
|
|
57
|
+
"vitest": "^2.1.3"
|
|
54
58
|
},
|
|
55
59
|
"engines": {
|
|
56
60
|
"node": ">=18.17.0"
|