@clerk/upgrade 0.0.1

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.
Files changed (120) hide show
  1. package/README.md +25 -0
  2. package/dist/app.js +136 -0
  3. package/dist/cli.js +50 -0
  4. package/dist/constants/sdks.js +46 -0
  5. package/dist/constants/versions.js +7 -0
  6. package/dist/scan.js +103 -0
  7. package/dist/test.js +100 -0
  8. package/dist/util/expandable-list.js +143 -0
  9. package/dist/util/generate-changelog.js +28 -0
  10. package/dist/util/get-clerk-version.js +7 -0
  11. package/dist/util/guess-framework.js +22 -0
  12. package/dist/util/load-change.js +31 -0
  13. package/dist/util/step-sequencer.js +31 -0
  14. package/dist/util/test.js +100 -0
  15. package/dist/versions/v5/backend/clerk-import.md +15 -0
  16. package/dist/versions/v5/backend/clockskewinseconds.md +7 -0
  17. package/dist/versions/v5/backend/createclerkclient-apikey.md +21 -0
  18. package/dist/versions/v5/backend/createclerkclient-frontendapi.md +21 -0
  19. package/dist/versions/v5/backend/externalaccount-picture.md +0 -0
  20. package/dist/versions/v5/backend/externalaccountjson-avatarurl.md +0 -0
  21. package/dist/versions/v5/backend/getpendingorganizationinvitationlist.md +12 -0
  22. package/dist/versions/v5/backend/organization-logourl.md +0 -0
  23. package/dist/versions/v5/backend/organizationjson-logourl.md +0 -0
  24. package/dist/versions/v5/backend/organizationmembershippublicuserdata-profileimageurl.md +0 -0
  25. package/dist/versions/v5/backend/organizationmembershippublicuserdatajson-profileimageurl.md +0 -0
  26. package/dist/versions/v5/backend/pkgversion.md +0 -0
  27. package/dist/versions/v5/backend/user-profileimageurl.md +0 -0
  28. package/dist/versions/v5/backend/userjson-profileimageurl.md +0 -0
  29. package/dist/versions/v5/chromeExtension/clerkprovider-tokencache.md +26 -0
  30. package/dist/versions/v5/common/api-key-to-secret-key.md +6 -0
  31. package/dist/versions/v5/common/clerkprovider-frontendapi.md +7 -0
  32. package/dist/versions/v5/common/frontend-api-to-publishable-key.md +6 -0
  33. package/dist/versions/v5/common/handlemagiclinkverification.md +7 -0
  34. package/dist/versions/v5/common/ismagiclinkerror.md +7 -0
  35. package/dist/versions/v5/common/magiclinkerror.md +7 -0
  36. package/dist/versions/v5/common/magiclinkerrorcode.md +7 -0
  37. package/dist/versions/v5/common/navigate-to-routerpush-routerreplace.md +16 -0
  38. package/dist/versions/v5/common/setsession.md +32 -0
  39. package/dist/versions/v5/common/usemagiclink.md +7 -0
  40. package/dist/versions/v5/expo/apikey-to-publishable-key.md +6 -0
  41. package/dist/versions/v5/expo/clerkprovider-frontendapi.md +17 -0
  42. package/dist/versions/v5/fastify/api-url-value-change.md +6 -0
  43. package/dist/versions/v5/fastify/clerkplugin-frontendapi.md +17 -0
  44. package/dist/versions/v5/fastify/createclerkclient-apikey.md +17 -0
  45. package/dist/versions/v5/fastify/createclerkclient-frontendapi.md +17 -0
  46. package/dist/versions/v5/gatsby/api-url-value-change.md +6 -0
  47. package/dist/versions/v5/gatsby/apikey-to-publishable-key.md +6 -0
  48. package/dist/versions/v5/gatsby/createclerkclient-apikey.md +17 -0
  49. package/dist/versions/v5/gatsby/createclerkclient-frontendapi.md +17 -0
  50. package/dist/versions/v5/gatsby/withserverauth-return-type.md +43 -0
  51. package/dist/versions/v5/index.js +63 -0
  52. package/dist/versions/v5/js/afterswitchorganizationurl.md +15 -0
  53. package/dist/versions/v5/js/appearance-organizationpreview-organizationswitcher.md +8 -0
  54. package/dist/versions/v5/js/experimental-canusecaptcha.md +6 -0
  55. package/dist/versions/v5/js/experimental-captchasitekey.md +6 -0
  56. package/dist/versions/v5/js/experimental-captchaurl.md +6 -0
  57. package/dist/versions/v5/js/external-account-avatarurl.md +7 -0
  58. package/dist/versions/v5/js/getorganizationmemberships.md +14 -0
  59. package/dist/versions/v5/js/lastorganizationinvitation-member.md +6 -0
  60. package/dist/versions/v5/js/organization-create-string.md +15 -0
  61. package/dist/versions/v5/js/organization-getpendinginvitations.md +14 -0
  62. package/dist/versions/v5/js/organization-logourl.md +7 -0
  63. package/dist/versions/v5/js/redirecttohome.md +13 -0
  64. package/dist/versions/v5/js/signup-attemptweb3walletverification-generatedsignature.md +20 -0
  65. package/dist/versions/v5/js/unstable-invitationupdate.md +6 -0
  66. package/dist/versions/v5/js/unstable-membershipupdate.md +6 -0
  67. package/dist/versions/v5/js/useorganization-invitationlist.md +25 -0
  68. package/dist/versions/v5/js/useorganization-membershiplist.md +24 -0
  69. package/dist/versions/v5/js/useorganizations.md +26 -0
  70. package/dist/versions/v5/js/user-createexternalaccount-redirecturl.md +7 -0
  71. package/dist/versions/v5/js/user-orgpublicdata-profileimageurl.md +7 -0
  72. package/dist/versions/v5/js/user-update-password.md +19 -0
  73. package/dist/versions/v5/js/userprofile-prop.md +7 -0
  74. package/dist/versions/v5/next/api-url-value-change.md +6 -0
  75. package/dist/versions/v5/next/auth-middleware-deprecated.md +10 -0
  76. package/dist/versions/v5/next/authmiddleware-apikey.md +17 -0
  77. package/dist/versions/v5/next/authmiddleware-frontendapi.md +9 -0
  78. package/dist/versions/v5/next/clerk-js-version-next-public.md +8 -0
  79. package/dist/versions/v5/next/createclerkclient-apikey.md +17 -0
  80. package/dist/versions/v5/next/createclerkclient-frontendapi.md +17 -0
  81. package/dist/versions/v5/next/getauth-apikey.md +9 -0
  82. package/dist/versions/v5/next/import-api-url.md +7 -0
  83. package/dist/versions/v5/next/import-api-version.md +7 -0
  84. package/dist/versions/v5/next/import-clerk-js-url.md +7 -0
  85. package/dist/versions/v5/next/import-clerk-js-version.md +7 -0
  86. package/dist/versions/v5/next/import-domain.md +7 -0
  87. package/dist/versions/v5/next/import-is-satellite.md +7 -0
  88. package/dist/versions/v5/next/import-nextjs-api.md +22 -0
  89. package/dist/versions/v5/next/import-nextjs-app-beta.md +10 -0
  90. package/dist/versions/v5/next/import-nextjs-edge-middleware.md +6 -0
  91. package/dist/versions/v5/next/import-nextjs-edge-middlewarefiles.md +6 -0
  92. package/dist/versions/v5/next/import-nextjs-ssr.md +7 -0
  93. package/dist/versions/v5/next/import-proxy-url.md +7 -0
  94. package/dist/versions/v5/next/import-publishable-key.md +7 -0
  95. package/dist/versions/v5/next/import-secret-key.md +7 -0
  96. package/dist/versions/v5/next/import-sign-in-url.md +7 -0
  97. package/dist/versions/v5/next/import-sign-up-url.md +6 -0
  98. package/dist/versions/v5/next/with-clerk-middleware-removed.md +10 -0
  99. package/dist/versions/v5/node/api-url-value-change.md +6 -0
  100. package/dist/versions/v5/node/cjs-esm-instance.md +16 -0
  101. package/dist/versions/v5/node/clerkexpressrequireauth-apikey.md +17 -0
  102. package/dist/versions/v5/node/clerkexpressrequireauth-frontendapi.md +17 -0
  103. package/dist/versions/v5/node/clerkexpresswithauth-apikey.md +17 -0
  104. package/dist/versions/v5/node/clerkexpresswithauth-frontendapi.md +17 -0
  105. package/dist/versions/v5/node/createclerkclient-apikey.md +17 -0
  106. package/dist/versions/v5/node/createclerkclient-frontendapi.md +17 -0
  107. package/dist/versions/v5/node/createclerkexpressrequireauth-apikey.md +17 -0
  108. package/dist/versions/v5/node/createclerkexpressrequireauth-frontendapi.md +17 -0
  109. package/dist/versions/v5/node/createclerkexpresswithauth-apikey.md +17 -0
  110. package/dist/versions/v5/node/createclerkexpresswithauth-frontendapi.md +17 -0
  111. package/dist/versions/v5/node/package-rename.md +6 -0
  112. package/dist/versions/v5/node/setclerkapikey.md +17 -0
  113. package/dist/versions/v5/node/setclerkapiversion.md +17 -0
  114. package/dist/versions/v5/node/setclerkhttpoptions.md +17 -0
  115. package/dist/versions/v5/node/setclerkserverapiurl.md +20 -0
  116. package/dist/versions/v5/react/api-url-value-change.md +6 -0
  117. package/dist/versions/v5/shared/buildrequesturl.md +14 -0
  118. package/dist/versions/v5/shared/organizationcontext.md +7 -0
  119. package/dist/versions/v5/shared/useorganizationlist-organizationlist.md +19 -0
  120. package/package.json +63 -0
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # @clerk/upgrade
2
+
3
+ A tool that helps with upgrading major versions of Clerk's SDKs.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx @clerk/upgrade
9
+ ```
10
+
11
+ ## Caveats
12
+
13
+ This tool uses regular expressions to scan for patterns that match breaking changes. This makes it run substantially faster and makes it more accessible for us at Clerk to author matchers for each breaking change, however it means that _we cannot gurarantee 100% accuracy of the results_. As such, it's important to treat this as a tool that can help you to complete your major version upgrades, rather than an automatic fix to all issues.
14
+
15
+ The main thing that this tool will miss is cases where _unusual import patterns_ are used in your codebase.As an example, if we made a breaking change to the `getAuth` function exported from `@clerk/nextjs`, we 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 running your imports like `import * as ClerkNext from "@clerk/nextjs"`, you could use `getAuth` without us detecting it with our matcher.
16
+
17
+ It will also be very likely to miss if you bind a method on an object to a separate variable and call it from there, or pass a bound method through a function param. For example, something like this:
18
+
19
+ ```js
20
+ const updateUser = user.update.bind(user);
21
+
22
+ updateUser({ username: 'foo' });
23
+ ```
24
+
25
+ Overall, there's a very good chance that this tool catches everything, but it's not a guarantee, so make sure that you also test your app before deploying, and that you have good e2e test coverage, which are good practices normally anyway, rather than blindly trusting that since the clerk upgrade tool was run, you are for sure covered.
package/dist/app.js ADDED
@@ -0,0 +1,136 @@
1
+ import React, { useState } from 'react';
2
+ import { Text, Newline } from 'ink';
3
+ import { Select, MultiSelect, TextInput } from '@inkjs/ui';
4
+ import SDKS from './constants/sdks.js';
5
+ import VERSIONS from './constants/versions.js';
6
+ import Scan from './scan.js';
7
+ import guessFrameworks from './util/guess-framework.js';
8
+ import getClerkMajorVersion from './util/get-clerk-version.js';
9
+ import Gradient from 'ink-gradient';
10
+ import BigText from 'ink-big-text';
11
+ export default function App({
12
+ _fromVersion,
13
+ _toVersion,
14
+ _sdk,
15
+ _dir = false,
16
+ _ignore = []
17
+ }) {
18
+ const [sdks, setSdks] = useState(_sdk ? [_sdk] : []);
19
+ const [sdkGuesses, setSdkGuesses] = useState([]);
20
+ const [sdkGuessConfirmed, setSdkGuessConfirmed] = useState(false);
21
+ const [sdkGuessAttempted, setSdkGuessAttempted] = useState(false);
22
+ const [fromVersion, setFromVersion] = useState(_fromVersion);
23
+ const [fromVersionGuessAttempted, fromVersionSdkGuessAttempted] = useState(false);
24
+ const [toVersion, setToVersion] = useState(_toVersion);
25
+ const [dir, setDir] = useState(_dir);
26
+ const [ignore, setIgnore] = useState(_ignore);
27
+ const [configComplete, setConfigComplete] = useState(false);
28
+ const [configVerified, setConfigVerified] = useState(false);
29
+ let fromVersionGuess = false;
30
+
31
+ // We try to guess which SDK they are using
32
+ if (isEmpty(sdks) && isEmpty(sdkGuesses) && !sdkGuessAttempted) {
33
+ if (!dir) return setDir(process.cwd());
34
+ setSdkGuesses(guessFrameworks(dir)); // this is the suspect line
35
+ setSdkGuessAttempted(true);
36
+ }
37
+
38
+ // We try to guess which version of Clerk they are using
39
+ if (!fromVersion && !fromVersionGuess && !fromVersionGuessAttempted) {
40
+ fromVersionGuess = getClerkMajorVersion();
41
+ setFromVersionGuessAttempted(true);
42
+ }
43
+
44
+ // No support for v3 or below, sadly
45
+ if (parseInt(fromVersion) < 4) {
46
+ return /*#__PURE__*/React.createElement(Text, {
47
+ color: "red"
48
+ }, "We're so sorry, but this tool only supports migration from version 4 and above.");
49
+ }
50
+
51
+ // If they are trying to/from the same version, that's an error
52
+ if (parseInt(fromVersion) === parseInt(toVersion)) {
53
+ return /*#__PURE__*/React.createElement(Text, {
54
+ color: "red"
55
+ }, "You are already on version ", toVersion, ", so there's no need to migrate!");
56
+ }
57
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Gradient, {
58
+ name: "vice"
59
+ }, /*#__PURE__*/React.createElement(BigText, {
60
+ text: "Clerk Upgrade",
61
+ font: "tiny"
62
+ })), !configComplete && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, /*#__PURE__*/React.createElement(Text, {
63
+ color: "blue"
64
+ }, "Hello friend!"), " We're excited to help you upgrade Clerk", fromVersion ? ` from v${fromVersion}` : '', toVersion ? ` to v${toVersion}` : '', ". 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, {
65
+ key: guess.value
66
+ }, ' ', "- ", 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, {
67
+ options: [{
68
+ label: 'yes',
69
+ value: 'yes'
70
+ }, {
71
+ label: 'no',
72
+ value: 'no'
73
+ }],
74
+ onChange: item => {
75
+ setSdkGuessConfirmed(true);
76
+ // if true, we were right so we set the sdk
77
+ if (item === 'yes') setSdks(sdkGuesses.map(guess => guess.value));
78
+ }
79
+ })), 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, {
80
+ color: "gray"
81
+ }, "(select with space bar, multiple can be selected, press enter when finished)"), /*#__PURE__*/React.createElement(MultiSelect, {
82
+ options: SDKS,
83
+ onSubmit: value => setSdks(value),
84
+ visibleOptionCount: SDKS.length
85
+ })), !isEmpty(sdks) > 0 && 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, {
86
+ color: "gray"
87
+ }, "(globstar syntax supported)"), /*#__PURE__*/React.createElement(TextInput, {
88
+ defaultValue: "**/*",
89
+ onSubmit: val => setDir(val)
90
+ })), !isEmpty(sdks) && fromVersion && toVersion && dir && isEmpty(ignore) && !configComplete && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, "Are there any files or directories you'd like to ignore? If so, you can add them below, separated by commas. We ignore \"node_modules\" and \".git\" by default."), /*#__PURE__*/React.createElement(Text, {
91
+ color: "gray"
92
+ }, "(globstar syntax supported)"), /*#__PURE__*/React.createElement(TextInput, {
93
+ placeholder: "docs/**, images/**",
94
+ defaultValue: ignore,
95
+ onSubmit: val => {
96
+ setIgnore(val.includes(',') ? val.split(/\s*,\s*/) : [].concat(val));
97
+ setConfigComplete(true);
98
+ }
99
+ })), configComplete && !configVerified && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, null, "Ok, here's our configuration:"), /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.createElement(Text, null, "Clerk ", sdks.length > 1 ? 'SDKs' : 'SDK', " used:", /*#__PURE__*/React.createElement(Text, {
100
+ color: "green"
101
+ }, " ", sdks.toString())), /*#__PURE__*/React.createElement(Text, null, "Migrating from", /*#__PURE__*/React.createElement(Text, {
102
+ color: "green"
103
+ }, " v", fromVersion, " "), "to", /*#__PURE__*/React.createElement(Text, {
104
+ color: "green"
105
+ }, " v", toVersion)), /*#__PURE__*/React.createElement(Text, null, "Looking in the directory", /*#__PURE__*/React.createElement(Text, {
106
+ color: "green"
107
+ }, " ", dir, " "), ignore.length > 0 && /*#__PURE__*/React.createElement(React.Fragment, null, "and ignoring", /*#__PURE__*/React.createElement(Text, {
108
+ color: "green"
109
+ }, " ", ignore.join(', ')))), /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.createElement(Text, null, "Does this look right?"), /*#__PURE__*/React.createElement(Select, {
110
+ options: [{
111
+ label: 'yes',
112
+ value: true
113
+ }, {
114
+ label: 'no - exit, and i will try again',
115
+ value: false
116
+ }],
117
+ onChange: value => {
118
+ if (!value) {
119
+ process.exit();
120
+ } else {
121
+ setConfigVerified(true);
122
+ }
123
+ }
124
+ })), configVerified && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Scan, {
125
+ fromVersion,
126
+ toVersion,
127
+ sdks,
128
+ dir,
129
+ ignore
130
+ })));
131
+ }
132
+
133
+ // small util to make the logic blocks easier to visually parse
134
+ function isEmpty(arr) {
135
+ return !arr.length;
136
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import React from 'react';
3
+ import { render } from 'ink';
4
+ import meow from 'meow';
5
+ import App from './app.js';
6
+ import sdks from './constants/sdks.js';
7
+ const cli = meow(`
8
+ Usage
9
+ $ clerk-upgrade
10
+
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
+
18
+ Examples
19
+ $ clerk-upgrade --from=4 --to=5 --sdk=nextjs --dir=src/**
20
+ Hello, Jane
21
+ `, {
22
+ importMeta: import.meta,
23
+ flags: {
24
+ from: {
25
+ type: 'string',
26
+ default: '4'
27
+ },
28
+ to: {
29
+ type: 'string',
30
+ default: '5'
31
+ },
32
+ sdk: {
33
+ type: 'string',
34
+ choices: sdks.map(i => i.value)
35
+ },
36
+ dir: {
37
+ type: 'string'
38
+ },
39
+ ignore: {
40
+ type: 'string',
41
+ isMultiple: true
42
+ }
43
+ }
44
+ });
45
+ render( /*#__PURE__*/React.createElement(App, {
46
+ _fromVersion: cli.flags.from,
47
+ _toVersion: cli.flags.to,
48
+ _sdk: cli.flags.sdk,
49
+ _dir: cli.flags.dir
50
+ }));
@@ -0,0 +1,46 @@
1
+ export default [{
2
+ label: '@clerk/nextjs',
3
+ value: 'nextjs'
4
+ }, {
5
+ label: '@clerk/remix',
6
+ value: 'remix'
7
+ }, {
8
+ label: 'gatsby-plugin-clerk',
9
+ value: 'gatsby'
10
+ }, {
11
+ label: '@clerk/redwood',
12
+ value: 'redwood'
13
+ }, {
14
+ label: '@clerk/react',
15
+ value: 'react'
16
+ }, {
17
+ label: '@clerk/expo',
18
+ value: 'expo'
19
+ }, {
20
+ label: '@clerk/fastify',
21
+ value: 'fastify'
22
+ }, {
23
+ label: '@clerk/express',
24
+ value: 'express'
25
+ }, {
26
+ label: '@clerk/clerk-js',
27
+ value: 'js'
28
+ }, {
29
+ label: '@clerk/clerk-sdk-node',
30
+ value: 'node'
31
+ }, {
32
+ label: '@clerk/backend',
33
+ value: 'backend'
34
+ }, {
35
+ label: '@clerk/localizations',
36
+ value: 'localizations'
37
+ }, {
38
+ label: '@clerk/shared',
39
+ value: 'shared'
40
+ }, {
41
+ label: '@clerk/types',
42
+ value: 'types'
43
+ }, {
44
+ label: '@clerk/chrome-extension',
45
+ value: 'chromeExtension'
46
+ }];
@@ -0,0 +1,7 @@
1
+ export default [{
2
+ label: 'v5',
3
+ value: 5
4
+ }, {
5
+ label: 'v4',
6
+ value: 4
7
+ }];
package/dist/scan.js ADDED
@@ -0,0 +1,103 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Text, Newline, Box } from 'ink';
3
+ import { globby } from 'globby';
4
+ import fs from 'fs/promises';
5
+ import { ProgressBar } from '@inkjs/ui';
6
+ import indexToPosition from 'index-to-position';
7
+ import ExpandableList from './util/expandable-list.js';
8
+ export default function Scan({
9
+ fromVersion,
10
+ toVersion,
11
+ sdks,
12
+ dir,
13
+ ignore
14
+ }) {
15
+ // NOTE: if the difference between fromVersion and toVersion is greater than 1
16
+ // we need to do a little extra work here and import two matchers,
17
+ // sequence them after each other, and clearly mark which version migration
18
+ // applies to each log.
19
+ //
20
+ // This is not yet implemented though since the current state of the script
21
+ // only handles a single version.
22
+ const [status, setStatus] = useState('Initializing');
23
+ const [progress, setProgress] = useState(0);
24
+ const [complete, setComplete] = useState(false);
25
+ const [matchers, setMatchers] = useState();
26
+ const [files, setFiles] = useState();
27
+ const [results, setResults] = useState([]);
28
+
29
+ // Load matchers
30
+ // -------------
31
+ // result = `matchers` set to format:
32
+ // { sdkName: [{ title: 'x', matcher: /x/, slug: 'x', ... }] }
33
+ useEffect(() => {
34
+ setStatus(`Loading data for v${toVersion} migration`);
35
+ import(`./versions/v${toVersion}/index.js`).then(version => {
36
+ setMatchers(sdks.reduce((m, sdk) => {
37
+ m[sdk] = version.default[sdk];
38
+ return m;
39
+ }, {}));
40
+ });
41
+ }, [toVersion]);
42
+
43
+ // Get all files from the glob matcher
44
+ // -----------------------------------
45
+ // result = `files` set to format: ['/filename', '/other/filename']
46
+ useEffect(() => {
47
+ setStatus('Collecting files to scan');
48
+ ignore.push('node_modules/**', '**/node_modules/**', '.git/**', 'package.json', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml');
49
+ globby(dir, {
50
+ ignore: [...ignore.filter(x => x)]
51
+ }).then(files => setFiles(files));
52
+ }, [dir, ignore]);
53
+
54
+ // Read files and scan regexes
55
+ // ---------------------------
56
+ // result = `results` set to format
57
+ //
58
+ useEffect(() => {
59
+ if (!matchers || !files) return;
60
+ Promise.all(
61
+ // first we read all the files
62
+ files.map(async (file, idx) => {
63
+ setStatus(`Scanning ${file}`);
64
+ setProgress(Math.ceil(idx / files.length * 100));
65
+ const content = await fs.readFile(file, 'utf8');
66
+
67
+ // then we run each of the matchers against the file contents
68
+ // TODO: combine results on the same match, add multiple file/positions
69
+ for (const sdk in matchers) {
70
+ matchers[sdk].map(matcher => {
71
+ const matches = content.matchAll(matcher.matcher);
72
+ if (!matches) return;
73
+ Array.from(matches).map(match => {
74
+ // TODO: index should be converted to line/col
75
+ results.push({
76
+ sdk,
77
+ file,
78
+ position: indexToPosition(content, match.index),
79
+ ...matcher
80
+ });
81
+ setResults(results);
82
+ });
83
+ });
84
+ }
85
+ })).then(() => {
86
+ setComplete(true);
87
+ if (results.length < 1) {
88
+ setStatus('It looks like you have nothing you need to change, upgrade away!');
89
+ } else {
90
+ setStatus('File scan complete. See results below!');
91
+ }
92
+ }).catch(err => {
93
+ console.error(err);
94
+ });
95
+ }, [matchers, files]);
96
+ return /*#__PURE__*/React.createElement(React.Fragment, null, complete ? /*#__PURE__*/React.createElement(Text, {
97
+ color: "green"
98
+ }, "\u2713 ", status) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ProgressBar, {
99
+ value: progress
100
+ }), /*#__PURE__*/React.createElement(Text, null, status)), /*#__PURE__*/React.createElement(Newline, null), !!results.length && /*#__PURE__*/React.createElement(ExpandableList, {
101
+ items: results
102
+ }));
103
+ }
package/dist/test.js ADDED
@@ -0,0 +1,100 @@
1
+ import React, { useReducer, useMemo } from 'react';
2
+ import { Text, Newline, useInput, Box, render } from 'ink';
3
+ function ExpandableList({
4
+ items
5
+ }) {
6
+ // add focused/expanded state on the set of items
7
+ const initialItems = useMemo(() => {
8
+ return items.map((i, idx) => {
9
+ i.focused = idx === 0 ? true : false;
10
+ i.expanded = false;
11
+ return i;
12
+ });
13
+ }, [items]);
14
+
15
+ // set up the focus/expanded state machine
16
+ const [state, dispatch] = useReducer(reducer, initialItems);
17
+
18
+ // this is what lets us respond to keyboard input
19
+ useInput((input, key) => {
20
+ if (key.downArrow) {
21
+ dispatch({
22
+ type: 'focus-next-option'
23
+ });
24
+ }
25
+ if (key.upArrow) {
26
+ dispatch({
27
+ type: 'focus-previous-option'
28
+ });
29
+ }
30
+ if (input === ' ') {
31
+ dispatch({
32
+ type: 'toggle-focused-option'
33
+ });
34
+ }
35
+ });
36
+
37
+ // and here's the actual markup we render for each list item!
38
+ return state.map(item => {
39
+ return /*#__PURE__*/React.createElement(Box, {
40
+ borderStyle: "round",
41
+ flexDirection: "column",
42
+ borderColor: item.focused ? 'blue' : 'white',
43
+ key: item.title
44
+ }, /*#__PURE__*/React.createElement(Text, {
45
+ bold: true
46
+ }, item.title), /*#__PURE__*/React.createElement(Text, null, "Location: ", result.file, ":", result.position.line, ":", result.position.column), item.expanded && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.createElement(Text, {
47
+ color: "gray"
48
+ }, "changed in ", item.sdk, " SDK"), /*#__PURE__*/React.createElement(Link, {
49
+ url: item.link
50
+ }, /*#__PURE__*/React.createElement(Text, null, "Migration guide entry \xBB")), /*#__PURE__*/React.createElement(Text, null, item.content)));
51
+ });
52
+ }
53
+
54
+ // I'd like to recognize that this logic is kinda crazy, but it works 💖
55
+ function reducer(state, action) {
56
+ // if the current item is focused and a next item exists
57
+ // un-focus it and tee up the next one to be focused
58
+ if (action.type === 'focus-next-option') {
59
+ let nextIdx;
60
+ return state.map((item, idx) => {
61
+ if (item.focused && state[idx + 1]) {
62
+ nextIdx = idx + 1;
63
+ item.focused = false;
64
+ return item;
65
+ }
66
+ if (idx === nextIdx) {
67
+ item.focused = true;
68
+ return item;
69
+ }
70
+ return item;
71
+ });
72
+ }
73
+
74
+ // if the next item is focused, focus this one and tee up the next one to be un-focused
75
+ if (action.type === 'focus-previous-option') {
76
+ let nextIdx;
77
+ return state.map((item, idx) => {
78
+ if (state[idx + 1]?.focused) {
79
+ item.focused = true;
80
+ nextIdx = idx + 1;
81
+ return item;
82
+ }
83
+ if (idx === nextIdx) {
84
+ item.focused = false;
85
+ return item;
86
+ }
87
+ return item;
88
+ });
89
+ }
90
+
91
+ // if the space bar is hit, toggle expand/contract on the currently focused item
92
+ if (action.type === 'toggle-focused-option') {
93
+ return state.map(item => {
94
+ if (item.focused) {
95
+ item.expanded = !item.expanded;
96
+ }
97
+ return item;
98
+ });
99
+ }
100
+ }
@@ -0,0 +1,143 @@
1
+ import React, { useReducer, useMemo } from 'react';
2
+ import { Text, Newline, useInput, Box } from 'ink';
3
+ import Link from 'ink-link';
4
+ import Markdown from 'ink-markdown';
5
+
6
+ // A listing of items which can be navigated with arrow keys and expanded/contracted
7
+ // with space bar. Limits the number visible at a time to prevent rendering issues with
8
+ // super long lists
9
+ export default function ExpandableList({
10
+ items,
11
+ numberVisible = 10
12
+ }) {
13
+ // set up our little state machine
14
+ const [state, dispatch] = useReducer(reducer, items, items => {
15
+ // add focused/expanded state on the set of items
16
+ const all = items.map((i, idx) => {
17
+ i.focused = idx === 0 ? true : false;
18
+ i.expanded = false;
19
+ return i;
20
+ });
21
+
22
+ // start idx, end idx
23
+ const visible = [0, numberVisible];
24
+ return {
25
+ numberVisible,
26
+ all,
27
+ visible
28
+ };
29
+ });
30
+
31
+ // this is what lets us respond to keyboard input
32
+ useInput((input, key) => {
33
+ if (key.downArrow) {
34
+ dispatch({
35
+ type: 'focus-next-option'
36
+ });
37
+ }
38
+ if (key.upArrow) {
39
+ dispatch({
40
+ type: 'focus-previous-option'
41
+ });
42
+ }
43
+ if (input === ' ') {
44
+ dispatch({
45
+ type: 'toggle-focused-option'
46
+ });
47
+ }
48
+ });
49
+
50
+ // and here's the actual markup we render for each list item!
51
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Text, {
52
+ color: "blue"
53
+ }, "Navigation Instructions:"), /*#__PURE__*/React.createElement(Text, null, "Navigate through items with \u2191 and \u2193 arrow keys. Expand the details of any item with space bar. \u2193 key on the last item goes to the next page, \u2191 on the first item goes to the previous page. To exit this interface, use \"control + c\"."), /*#__PURE__*/React.createElement(Newline, null), state.all.reduce((memo, item, idx) => {
54
+ if (idx < state.visible[0] || idx >= state.visible[1]) return memo;
55
+ const loc = `${item.file}:${item.position.line}:${item.position.column}`;
56
+ memo.push( /*#__PURE__*/React.createElement(Box, {
57
+ borderStyle: item.focused ? 'double' : 'single',
58
+ flexDirection: "column",
59
+ borderColor: item.focused ? 'blue' : 'white',
60
+ paddingX: 1,
61
+ key: loc
62
+ }, /*#__PURE__*/React.createElement(Markdown, null, item.title), /*#__PURE__*/React.createElement(Text, null, "Location: ", loc), item.expanded && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Newline, null), /*#__PURE__*/React.createElement(Markdown, null, item.content), /*#__PURE__*/React.createElement(Link, {
63
+ url: item.link
64
+ }, /*#__PURE__*/React.createElement(Text, null, "See in migration guide \xBB")))));
65
+ return memo;
66
+ }, []), state.all.length > state.numberVisible && /*#__PURE__*/React.createElement(Text, null, "Showing ", state.visible[0] + 1, " - ", Math.min(state.visible[1], state.all.length), " of ", state.all.length));
67
+ }
68
+
69
+ // I'd like to recognize that this logic is kinda crazy, but it works 💖
70
+ function reducer(state, action) {
71
+ if (action.type === 'focus-next-option') {
72
+ let nextIdx;
73
+
74
+ // if the current item is focused and a next item exists
75
+ // un-focus it and tee up the next one to be focused
76
+ const all = state.all.map((item, idx) => {
77
+ if (item.focused && state.all[idx + 1]) {
78
+ nextIdx = idx + 1;
79
+ item.focused = false;
80
+ return item;
81
+ }
82
+ if (idx === nextIdx) {
83
+ item.focused = true;
84
+ return item;
85
+ }
86
+ return item;
87
+ });
88
+
89
+ // if we're scrolling past the last item in the list, shift the visible window
90
+ let visible = state.visible;
91
+ if (nextIdx >= state.visible[1]) {
92
+ visible = [state.visible[0] + state.numberVisible, state.visible[1] + state.numberVisible];
93
+ }
94
+ return {
95
+ all,
96
+ visible,
97
+ numberVisible: state.numberVisible
98
+ };
99
+ }
100
+ if (action.type === 'focus-previous-option') {
101
+ let nextIdx;
102
+
103
+ // if the next item is focused, focus this one and tee up the next one to be un-focused
104
+ const all = state.all.map((item, idx) => {
105
+ if (state.all[idx + 1]?.focused) {
106
+ item.focused = true;
107
+ nextIdx = idx + 1;
108
+ return item;
109
+ }
110
+ if (idx === nextIdx) {
111
+ item.focused = false;
112
+ return item;
113
+ }
114
+ return item;
115
+ });
116
+
117
+ // if we're scrolling to before the first item in the list, shift the visible window
118
+ let visible = state.visible;
119
+ if (nextIdx - 1 < state.visible[0]) {
120
+ visible = [state.visible[0] - state.numberVisible, state.visible[1] - state.numberVisible];
121
+ }
122
+ return {
123
+ all,
124
+ visible,
125
+ numberVisible: state.numberVisible
126
+ };
127
+ }
128
+
129
+ // if the space bar is hit, toggle expand/contract on the currently focused item
130
+ if (action.type === 'toggle-focused-option') {
131
+ const all = state.all.map(item => {
132
+ if (item.focused) {
133
+ item.expanded = !item.expanded;
134
+ }
135
+ return item;
136
+ });
137
+ return {
138
+ all,
139
+ visible: state.visible,
140
+ numberVisible: state.numberVisible
141
+ };
142
+ }
143
+ }
@@ -0,0 +1,28 @@
1
+ import SDKS from '../constants/sdks.js';
2
+ import { parseInline } from 'marked';
3
+ const VERSION = 'v5';
4
+ async function generate() {
5
+ let output = '';
6
+ output += `# Clerk SDKs ${VERSION} Changelog\n\n`;
7
+ output += `Below is a listing of all the changes made to Clerk SDKs in ${VERSION} - each section describes the changes to one SDK. Note that some changes may be repeated between different SDKs.\n\n`;
8
+ const data = (await import(`../versions/${VERSION}/index.js`)).default;
9
+ for (let sdk in data) {
10
+ output += `### ${getSdkName(sdk)}\n\n---\n\n`;
11
+ data[sdk].map(entry => {
12
+ // Note: make sure to add a custom anchor link here to match the cli output
13
+ // anchor should be `entry.slug`
14
+ output += `<details>
15
+ <summary>${parseInline(entry.title)}</summary>
16
+
17
+ Regex matcher: \`${entry.matcher}\`
18
+
19
+ ${entry.content}
20
+ </details>\n\n`;
21
+ });
22
+ }
23
+ return output;
24
+ }
25
+ generate().then(console.log);
26
+ function getSdkName(val) {
27
+ return SDKS.find(sdk => val === sdk.value).label;
28
+ }
@@ -0,0 +1,7 @@
1
+ import { readPackageSync } from 'read-pkg';
2
+ import semverRegex from 'semver-regex';
3
+ export default function getClerkMajorVersion() {
4
+ const pkg = readPackageSync();
5
+ const clerk = pkg.dependencies.clerk;
6
+ return clerk ? semverRegex.exec(clerk)[0][0] : false;
7
+ }
@@ -0,0 +1,22 @@
1
+ import { readPackageSync } from 'read-pkg';
2
+ import SDKS from '../constants/sdks.js';
3
+ export default function guessFrameworks(dir) {
4
+ const pkg = readPackageSync({
5
+ cwd: dir
6
+ });
7
+
8
+ // no guessing if there are no deps
9
+ if (!pkg.dependencies && !pkg.devDependencies) return [];
10
+ const deps = pkg.dependencies ? Object.keys(pkg.dependencies) : [];
11
+ const devDeps = pkg.devDependencies ? Object.keys(pkg.devDependencies) : [];
12
+ return SDKS.reduce((m, {
13
+ label,
14
+ value
15
+ }) => {
16
+ if (deps.includes(label) || devDeps.includes(label)) m.push({
17
+ label,
18
+ value
19
+ });
20
+ return m;
21
+ }, []);
22
+ }