@trpc/upgrade 0.0.0-alpha.2 → 0.0.0-alpha.26
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 +3 -0
- package/dist/cli.cjs +44 -20
- package/dist/transforms/hooksToOptions.cjs +54 -6
- package/dist/transforms/provider.cjs +9 -1
- package/package.json +5 -5
- package/src/bin/cli.ts +84 -26
- package/src/lib/ast/modifiers.ts +28 -0
- package/src/lib/ast/walkers.ts +18 -0
- package/src/transforms/hooksToOptions.ts +28 -5
- package/src/transforms/provider.ts +13 -1
package/README.md
CHANGED
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var path = require('path');
|
|
2
|
+
var path = require('node:path');
|
|
3
3
|
var cli$1 = require('@effect/cli');
|
|
4
4
|
var platform = require('@effect/platform');
|
|
5
5
|
var platformNode = require('@effect/platform-node');
|
|
@@ -10,22 +10,33 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
10
10
|
|
|
11
11
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
12
12
|
|
|
13
|
-
var version = "0.0.0-alpha.
|
|
13
|
+
var version = "0.0.0-alpha.25";
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const MakeCommand = (command, ...args)=>{
|
|
16
|
+
return platform.Command.make(command, ...args).pipe(platform.Command.workingDirectory(process.cwd()), platform.Command.runInShell(true));
|
|
17
|
+
};
|
|
18
|
+
const assertCleanGitTree = platform.Command.string(MakeCommand('git', 'status')).pipe().pipe(effect.Effect.filterOrFail(effect.String.includes('nothing to commit'), ()=>'Git tree is not clean, please commit your changes and try again, or run with `--force`'));
|
|
19
|
+
const getPackageManager = ()=>effect.Match.value(process.env.npm_config_user_agent ?? 'npm').pipe(effect.Match.when(effect.String.startsWith('pnpm'), ()=>'pnpm'), effect.Match.when(effect.String.startsWith('yarn'), ()=>'yarn'), effect.Match.when(effect.String.startsWith('bun'), ()=>'bun'), effect.Match.orElse(()=>'npm'));
|
|
16
20
|
const installPackage = (packageName)=>{
|
|
17
|
-
const packageManager =
|
|
18
|
-
return platform.Command.streamLines(
|
|
21
|
+
const packageManager = getPackageManager();
|
|
22
|
+
return platform.Command.streamLines(MakeCommand(packageManager, 'install', packageName)).pipe(effect.Stream.mapEffect(effect.Console.log), effect.Stream.runDrain);
|
|
23
|
+
};
|
|
24
|
+
const uninstallPackage = (packageName)=>{
|
|
25
|
+
const packageManager = getPackageManager();
|
|
26
|
+
const uninstallCmd = packageManager === 'yarn' ? 'remove' : 'uninstall';
|
|
27
|
+
return platform.Command.streamLines(MakeCommand(packageManager, uninstallCmd, packageName)).pipe(effect.Stream.mapEffect(effect.Console.log), effect.Stream.runDrain);
|
|
19
28
|
};
|
|
20
29
|
const filterIgnored = (files)=>effect.Effect.gen(function*() {
|
|
21
|
-
const ignores = yield* platform.Command.string(
|
|
22
|
-
yield* effect.Effect.
|
|
23
|
-
yield* effect.Effect.
|
|
30
|
+
const ignores = yield* platform.Command.string(MakeCommand('git', 'check-ignore', '**/*').pipe(platform.Command.runInShell(true))).pipe(effect.Effect.tap((_)=>effect.Effect.logDebug('Ignored files output:', _)), effect.Effect.map((_)=>_.split('\n')));
|
|
31
|
+
yield* effect.Effect.logDebug('cwd:', process.cwd());
|
|
32
|
+
yield* effect.Effect.logDebug('All files in program:', files.map((_)=>_.fileName));
|
|
33
|
+
yield* effect.Effect.logDebug('Ignored files:', ignores);
|
|
24
34
|
// Ignore "common files"
|
|
25
35
|
const filteredSourcePaths = files.filter((source)=>source.fileName.startsWith(path__default.default.resolve()) && // only look ahead of current directory
|
|
26
36
|
!source.fileName.includes('/trpc/packages/') && // relative paths when running codemod locally
|
|
37
|
+
!source.fileName.includes('/node_modules/') && // always ignore node_modules
|
|
27
38
|
!ignores.includes(source.fileName)).map((source)=>source.fileName);
|
|
28
|
-
yield* effect.Effect.
|
|
39
|
+
yield* effect.Effect.logDebug('Filtered files:', filteredSourcePaths);
|
|
29
40
|
return filteredSourcePaths;
|
|
30
41
|
});
|
|
31
42
|
const TSProgram = effect.Effect.succeed(typescript.findConfigFile(process.cwd(), typescript.sys.fileExists)).pipe(effect.Effect.filterOrFail(effect.Predicate.isNotNullable, ()=>'No tsconfig found'), effect.Effect.tap((_)=>effect.Effect.logDebug('Using tsconfig', _)), effect.Effect.map((_)=>typescript.readConfigFile(_, typescript.sys.readFile)), effect.Effect.map((_)=>typescript.parseJsonConfigFileContent(_.config, typescript.sys, process.cwd())), effect.Effect.map((_)=>typescript.createProgram({
|
|
@@ -40,15 +51,22 @@ const force = cli$1.Options.boolean('force').pipe(cli$1.Options.withAlias('f'),
|
|
|
40
51
|
* TODO: Instead of default values these should be detected automatically from the TS program
|
|
41
52
|
*/ const trpcFile = cli$1.Options.text('trpcFile').pipe(cli$1.Options.withAlias('f'), cli$1.Options.withDefault('~/trpc'), cli$1.Options.withDescription('Path to the trpc import file'));
|
|
42
53
|
const trpcImportName = cli$1.Options.text('trpcImportName').pipe(cli$1.Options.withAlias('i'), cli$1.Options.withDefault('trpc'), cli$1.Options.withDescription('Name of the trpc import'));
|
|
54
|
+
const skipTanstackQuery = cli$1.Options.boolean('skipTanstackQuery').pipe(cli$1.Options.withAlias('q'), cli$1.Options.withDefault(false), cli$1.Options.withDescription('Skip installing @trpc/tanstack-react-query package'));
|
|
55
|
+
const verbose = cli$1.Options.boolean('verbose').pipe(cli$1.Options.withAlias('v'), cli$1.Options.withDefault(false), cli$1.Options.withDescription('Enable verbose logging'));
|
|
43
56
|
const rootComamnd = cli$1.Command.make('upgrade', {
|
|
44
57
|
force,
|
|
45
58
|
trpcFile,
|
|
46
|
-
trpcImportName
|
|
59
|
+
trpcImportName,
|
|
60
|
+
skipTanstackQuery,
|
|
61
|
+
verbose
|
|
47
62
|
}, (args)=>effect.Effect.gen(function*() {
|
|
63
|
+
if (args.verbose) {
|
|
64
|
+
yield* effect.Effect.log('Running upgrade with args:', args);
|
|
65
|
+
}
|
|
48
66
|
if (!args.force) {
|
|
49
67
|
yield* assertCleanGitTree;
|
|
50
68
|
}
|
|
51
|
-
const transforms = yield* effect.
|
|
69
|
+
const transforms = yield* effect.pipe(cli$1.Prompt.multiSelect({
|
|
52
70
|
message: 'Select transforms to run',
|
|
53
71
|
choices: [
|
|
54
72
|
{
|
|
@@ -60,20 +78,26 @@ const rootComamnd = cli$1.Command.make('upgrade', {
|
|
|
60
78
|
value: require.resolve(transformPath('../transforms/provider.ts'))
|
|
61
79
|
}
|
|
62
80
|
]
|
|
63
|
-
}),
|
|
64
|
-
|
|
81
|
+
}), effect.Effect.flatMap((selected)=>{
|
|
82
|
+
if (selected.length === 0) {
|
|
83
|
+
return effect.Effect.fail(new Error('Please select at least one transform to run'));
|
|
84
|
+
}
|
|
85
|
+
return effect.Effect.succeed(selected);
|
|
86
|
+
}), effect.Effect.map(// Make sure provider transform runs first if it's selected
|
|
87
|
+
effect.Array.sortWith((a)=>!a.includes('provider.ts'), effect.Order.boolean)));
|
|
65
88
|
const program = yield* TSProgram;
|
|
66
89
|
const sourceFiles = program.getSourceFiles();
|
|
67
90
|
const commitedFiles = yield* filterIgnored(sourceFiles);
|
|
68
91
|
yield* effect.Effect.forEach(transforms, (transform)=>{
|
|
69
|
-
return effect.pipe(effect.Effect.log('Running transform', transform), effect.Effect.flatMap(()=>effect.Effect.tryPromise(async ()=>import('jscodeshift/src/Runner.js').then(({ run })=>run(transform, commitedFiles,
|
|
70
|
-
...args,
|
|
71
|
-
verbose: true
|
|
72
|
-
})))), effect.Effect.map((_)=>effect.Effect.log('Transform result', _)));
|
|
92
|
+
return effect.pipe(effect.Effect.log('Running transform', transform), effect.Effect.flatMap(()=>effect.Effect.tryPromise(async ()=>import('jscodeshift/src/Runner.js').then(({ run })=>run(transform, commitedFiles, args)))), effect.Effect.map((_)=>effect.Effect.log('Transform result', _)));
|
|
73
93
|
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
94
|
+
if (!args.skipTanstackQuery) {
|
|
95
|
+
yield* effect.Effect.log('Installing @trpc/tanstack-react-query');
|
|
96
|
+
yield* installPackage('@trpc/tanstack-react-query');
|
|
97
|
+
yield* effect.Effect.log('Uninstalling @trpc/react-query');
|
|
98
|
+
yield* uninstallPackage('@trpc/react-query');
|
|
99
|
+
}
|
|
100
|
+
}).pipe(effect.Logger.withMinimumLogLevel(args.verbose ? effect.LogLevel.Debug : effect.LogLevel.Info)));
|
|
77
101
|
const cli = cli$1.Command.run(rootComamnd, {
|
|
78
102
|
name: 'tRPC Upgrade CLI',
|
|
79
103
|
version: `v${version}`
|
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Replaces the identifier for the root path key
|
|
5
|
+
* of a member expression
|
|
6
|
+
*
|
|
7
|
+
* For dot notation like `rootKey.x.y.z` the AST
|
|
8
|
+
* is constructed with the `rootKey` being nested deep
|
|
9
|
+
* inside a wrapper MemberExpression holding `rootKey.x`
|
|
10
|
+
* and so on
|
|
11
|
+
*
|
|
12
|
+
* This function helps replace the `rootKey` identifier with
|
|
13
|
+
* the provided identifier node
|
|
14
|
+
*/ function replaceMemberExpressionRootIndentifier(j, expr, id) {
|
|
15
|
+
if (j.Identifier.check(expr.object)) {
|
|
16
|
+
expr.object = id;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (j.MemberExpression.check(expr.object)) {
|
|
20
|
+
return replaceMemberExpressionRootIndentifier(j, expr.object, id);
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Walks the path upwards to look for the closest parent
|
|
27
|
+
* of the mentioned type
|
|
28
|
+
*/ function findParentOfType(path, type) {
|
|
29
|
+
if (!type.check(path.node)) {
|
|
30
|
+
return findParentOfType(path.parentPath, type);
|
|
31
|
+
}
|
|
32
|
+
if (!path.parent) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return path;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const hookToOptions = {
|
|
4
39
|
useQuery: {
|
|
5
40
|
lib: '@tanstack/react-query',
|
|
6
41
|
fn: 'queryOptions'
|
|
@@ -155,23 +190,36 @@ function transform(file, api, options) {
|
|
|
155
190
|
name: oldIdentifier.name
|
|
156
191
|
}).forEach((path)=>{
|
|
157
192
|
if (j.MemberExpression.check(path.parent?.parent?.node)) {
|
|
158
|
-
const callExprPath = path.
|
|
193
|
+
const callExprPath = findParentOfType(path.parentPath, j.CallExpression);
|
|
194
|
+
if (!callExprPath) {
|
|
195
|
+
console.warn(`Failed to walk up the tree to find utilMethod call expression, on file: ${file.path}`, callExprPath, {
|
|
196
|
+
start: path.node.loc?.start,
|
|
197
|
+
end: path.node.loc?.end
|
|
198
|
+
});
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
159
201
|
const callExpr = callExprPath.node;
|
|
160
202
|
const memberExpr = callExpr.callee;
|
|
161
203
|
if (!j.CallExpression.check(callExpr) || !j.MemberExpression.check(memberExpr)) {
|
|
162
|
-
console.warn(
|
|
204
|
+
console.warn(`Failed to walk up the tree to find utilMethod with a \`trpc.PATH.<call>\`, on file: ${file.path}`, callExpr, {
|
|
205
|
+
start: path.node.loc?.start,
|
|
206
|
+
end: path.node.loc?.end
|
|
207
|
+
});
|
|
163
208
|
return;
|
|
164
209
|
}
|
|
165
|
-
if (!(j.MemberExpression.check(memberExpr.object) && j.Identifier.check(memberExpr.
|
|
210
|
+
if (!(j.MemberExpression.check(memberExpr.object) && j.Identifier.check(memberExpr.property) && memberExpr.property.name in utilMap)) {
|
|
166
211
|
console.warn('Failed to identify utilMethod from proxy call expression', memberExpr);
|
|
167
212
|
return;
|
|
168
213
|
}
|
|
169
214
|
// Replace util.PATH.proxyMethod() with trpc.PATH.queryFilter()
|
|
170
215
|
const proxyMethod = memberExpr.property.name;
|
|
171
|
-
|
|
216
|
+
const replacedPath = replaceMemberExpressionRootIndentifier(j, memberExpr, j.identifier(trpcImportName));
|
|
217
|
+
if (!replacedPath) {
|
|
218
|
+
console.warn('Failed to wrap proxy call expression', memberExpr);
|
|
219
|
+
}
|
|
172
220
|
memberExpr.property = j.identifier('queryFilter');
|
|
173
221
|
// Wrap it in queryClient.utilMethod()
|
|
174
|
-
|
|
222
|
+
callExprPath.replace(j.memberExpression(j.identifier('queryClient'), j.callExpression(j.identifier(utilMap[proxyMethod]), [
|
|
175
223
|
callExpr
|
|
176
224
|
])));
|
|
177
225
|
}
|
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
2
2
|
|
|
3
3
|
function transform(file, api, options) {
|
|
4
4
|
const { trpcImportName } = options;
|
|
5
|
+
let routerName = undefined;
|
|
5
6
|
const j = api.jscodeshift;
|
|
6
7
|
const root = j(file.source);
|
|
7
8
|
let dirtyFlag = false;
|
|
@@ -10,6 +11,8 @@ function transform(file, api, options) {
|
|
|
10
11
|
const declaration = path.node.declarations[0];
|
|
11
12
|
if (j.Identifier.check(declaration.id) && declaration.id.name === trpcImportName) {
|
|
12
13
|
if (j.CallExpression.check(declaration.init) && j.Identifier.check(declaration.init.callee) && declaration.init.callee.name === 'createTRPCReact') {
|
|
14
|
+
// Get router name ( TODO : should probably get this from the TS compiler along with the import path)
|
|
15
|
+
routerName = declaration.init.original?.typeParameters?.params?.[0]?.typeName?.name;
|
|
13
16
|
// Replace the `createTRPCReact` call with `createTRPCContext`
|
|
14
17
|
declaration.init.callee.name = 'createTRPCContext';
|
|
15
18
|
// Destructure the result into `TRPCProvider` and `useTRPC`
|
|
@@ -46,7 +49,7 @@ function transform(file, api, options) {
|
|
|
46
49
|
});
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
|
-
// Replace trpc.createClient with createTRPCClient
|
|
52
|
+
// Replace trpc.createClient with createTRPCClient<TRouter>
|
|
50
53
|
root.find(j.CallExpression, {
|
|
51
54
|
callee: {
|
|
52
55
|
object: {
|
|
@@ -59,6 +62,11 @@ function transform(file, api, options) {
|
|
|
59
62
|
}).forEach((path)=>{
|
|
60
63
|
path.node.callee = j.identifier('createTRPCClient');
|
|
61
64
|
dirtyFlag = true;
|
|
65
|
+
if (routerName) {
|
|
66
|
+
path.node.typeParameters = j.tsTypeParameterInstantiation([
|
|
67
|
+
j.tsTypeReference(j.identifier(routerName))
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
62
70
|
});
|
|
63
71
|
// Replace <trpc.Provider client={...} with <TRPCProvider trpcClient={...}
|
|
64
72
|
root.find(j.JSXElement, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trpc/upgrade",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.26",
|
|
4
4
|
"description": "Upgrade scripts for tRPC",
|
|
5
5
|
"author": "juliusmarminge",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
"!**/__tests__"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@effect/cli": "0.
|
|
32
|
-
"@effect/platform": "0.
|
|
33
|
-
"@effect/platform-node": "0.
|
|
34
|
-
"effect": "3.
|
|
31
|
+
"@effect/cli": "0.55.0",
|
|
32
|
+
"@effect/platform": "0.76.0",
|
|
33
|
+
"@effect/platform-node": "0.72.0",
|
|
34
|
+
"effect": "3.12.11",
|
|
35
35
|
"jscodeshift": "17.1.1",
|
|
36
36
|
"typescript": "^5.6.2"
|
|
37
37
|
},
|
package/src/bin/cli.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
|
-
import path from 'path';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import { Command as CLICommand, Options, Prompt } from '@effect/cli';
|
|
4
4
|
import { Command } from '@effect/platform';
|
|
5
5
|
import { NodeContext, NodeRuntime } from '@effect/platform-node';
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
Array,
|
|
8
8
|
Console,
|
|
9
9
|
Effect,
|
|
10
|
+
Logger,
|
|
11
|
+
LogLevel,
|
|
10
12
|
Match,
|
|
11
13
|
Order,
|
|
12
14
|
pipe,
|
|
@@ -24,39 +26,61 @@ import {
|
|
|
24
26
|
} from 'typescript';
|
|
25
27
|
import { version } from '../../package.json';
|
|
26
28
|
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
);
|
|
29
|
+
const MakeCommand = (command: string, ...args: string[]) => {
|
|
30
|
+
return Command.make(command, ...args).pipe(
|
|
31
|
+
Command.workingDirectory(process.cwd()),
|
|
32
|
+
Command.runInShell(true),
|
|
33
|
+
);
|
|
34
|
+
};
|
|
34
35
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
const assertCleanGitTree = Command.string(MakeCommand('git', 'status'))
|
|
37
|
+
.pipe()
|
|
38
|
+
.pipe(
|
|
39
|
+
Effect.filterOrFail(
|
|
40
|
+
String.includes('nothing to commit'),
|
|
41
|
+
() =>
|
|
42
|
+
'Git tree is not clean, please commit your changes and try again, or run with `--force`',
|
|
43
|
+
),
|
|
44
|
+
);
|
|
45
|
+
const getPackageManager = () =>
|
|
46
|
+
Match.value(process.env.npm_config_user_agent ?? 'npm').pipe(
|
|
39
47
|
Match.when(String.startsWith('pnpm'), () => 'pnpm'),
|
|
40
48
|
Match.when(String.startsWith('yarn'), () => 'yarn'),
|
|
41
49
|
Match.when(String.startsWith('bun'), () => 'bun'),
|
|
42
50
|
Match.orElse(() => 'npm'),
|
|
43
51
|
);
|
|
52
|
+
|
|
53
|
+
const installPackage = (packageName: string) => {
|
|
54
|
+
const packageManager = getPackageManager();
|
|
55
|
+
return Command.streamLines(
|
|
56
|
+
MakeCommand(packageManager, 'install', packageName),
|
|
57
|
+
).pipe(Stream.mapEffect(Console.log), Stream.runDrain);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const uninstallPackage = (packageName: string) => {
|
|
61
|
+
const packageManager = getPackageManager();
|
|
62
|
+
const uninstallCmd = packageManager === 'yarn' ? 'remove' : 'uninstall';
|
|
44
63
|
return Command.streamLines(
|
|
45
|
-
|
|
64
|
+
MakeCommand(packageManager, uninstallCmd, packageName),
|
|
46
65
|
).pipe(Stream.mapEffect(Console.log), Stream.runDrain);
|
|
47
66
|
};
|
|
48
67
|
|
|
49
68
|
const filterIgnored = (files: readonly SourceFile[]) =>
|
|
50
69
|
Effect.gen(function* () {
|
|
51
70
|
const ignores = yield* Command.string(
|
|
52
|
-
|
|
53
|
-
).pipe(
|
|
71
|
+
MakeCommand('git', 'check-ignore', '**/*').pipe(Command.runInShell(true)),
|
|
72
|
+
).pipe(
|
|
73
|
+
Effect.tap((_) => Effect.logDebug('Ignored files output:', _)),
|
|
74
|
+
Effect.map((_) => _.split('\n')),
|
|
75
|
+
);
|
|
54
76
|
|
|
55
|
-
yield* Effect.
|
|
77
|
+
yield* Effect.logDebug('cwd:', process.cwd());
|
|
78
|
+
|
|
79
|
+
yield* Effect.logDebug(
|
|
56
80
|
'All files in program:',
|
|
57
81
|
files.map((_) => _.fileName),
|
|
58
82
|
);
|
|
59
|
-
yield* Effect.
|
|
83
|
+
yield* Effect.logDebug('Ignored files:', ignores);
|
|
60
84
|
|
|
61
85
|
// Ignore "common files"
|
|
62
86
|
const filteredSourcePaths = files
|
|
@@ -64,11 +88,12 @@ const filterIgnored = (files: readonly SourceFile[]) =>
|
|
|
64
88
|
(source) =>
|
|
65
89
|
source.fileName.startsWith(path.resolve()) && // only look ahead of current directory
|
|
66
90
|
!source.fileName.includes('/trpc/packages/') && // relative paths when running codemod locally
|
|
91
|
+
!source.fileName.includes('/node_modules/') && // always ignore node_modules
|
|
67
92
|
!ignores.includes(source.fileName), // ignored files
|
|
68
93
|
)
|
|
69
94
|
.map((source) => source.fileName);
|
|
70
95
|
|
|
71
|
-
yield* Effect.
|
|
96
|
+
yield* Effect.logDebug('Filtered files:', filteredSourcePaths);
|
|
72
97
|
|
|
73
98
|
return filteredSourcePaths;
|
|
74
99
|
});
|
|
@@ -114,20 +139,36 @@ const trpcImportName = Options.text('trpcImportName').pipe(
|
|
|
114
139
|
Options.withDescription('Name of the trpc import'),
|
|
115
140
|
);
|
|
116
141
|
|
|
142
|
+
const skipTanstackQuery = Options.boolean('skipTanstackQuery').pipe(
|
|
143
|
+
Options.withAlias('q'),
|
|
144
|
+
Options.withDefault(false),
|
|
145
|
+
Options.withDescription('Skip installing @trpc/tanstack-react-query package'),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const verbose = Options.boolean('verbose').pipe(
|
|
149
|
+
Options.withAlias('v'),
|
|
150
|
+
Options.withDefault(false),
|
|
151
|
+
Options.withDescription('Enable verbose logging'),
|
|
152
|
+
);
|
|
153
|
+
|
|
117
154
|
const rootComamnd = CLICommand.make(
|
|
118
155
|
'upgrade',
|
|
119
156
|
{
|
|
120
157
|
force,
|
|
121
158
|
trpcFile,
|
|
122
159
|
trpcImportName,
|
|
160
|
+
skipTanstackQuery,
|
|
161
|
+
verbose,
|
|
123
162
|
},
|
|
124
163
|
(args) =>
|
|
125
164
|
Effect.gen(function* () {
|
|
165
|
+
if (args.verbose) {
|
|
166
|
+
yield* Effect.log('Running upgrade with args:', args);
|
|
167
|
+
}
|
|
126
168
|
if (!args.force) {
|
|
127
169
|
yield* assertCleanGitTree;
|
|
128
170
|
}
|
|
129
|
-
|
|
130
|
-
const transforms = yield* Effect.map(
|
|
171
|
+
const transforms = yield* pipe(
|
|
131
172
|
Prompt.multiSelect({
|
|
132
173
|
message: 'Select transforms to run',
|
|
133
174
|
choices: [
|
|
@@ -145,8 +186,18 @@ const rootComamnd = CLICommand.make(
|
|
|
145
186
|
},
|
|
146
187
|
],
|
|
147
188
|
}),
|
|
148
|
-
|
|
149
|
-
|
|
189
|
+
Effect.flatMap((selected) => {
|
|
190
|
+
if (selected.length === 0) {
|
|
191
|
+
return Effect.fail(
|
|
192
|
+
new Error('Please select at least one transform to run'),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return Effect.succeed(selected);
|
|
196
|
+
}),
|
|
197
|
+
Effect.map(
|
|
198
|
+
// Make sure provider transform runs first if it's selected
|
|
199
|
+
Array.sortWith((a) => !a.includes('provider.ts'), Order.boolean),
|
|
200
|
+
),
|
|
150
201
|
);
|
|
151
202
|
|
|
152
203
|
const program = yield* TSProgram;
|
|
@@ -159,7 +210,7 @@ const rootComamnd = CLICommand.make(
|
|
|
159
210
|
Effect.flatMap(() =>
|
|
160
211
|
Effect.tryPromise(async () =>
|
|
161
212
|
import('jscodeshift/src/Runner.js').then(({ run }) =>
|
|
162
|
-
run(transform, commitedFiles,
|
|
213
|
+
run(transform, commitedFiles, args),
|
|
163
214
|
),
|
|
164
215
|
),
|
|
165
216
|
),
|
|
@@ -167,9 +218,16 @@ const rootComamnd = CLICommand.make(
|
|
|
167
218
|
);
|
|
168
219
|
});
|
|
169
220
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
221
|
+
if (!args.skipTanstackQuery) {
|
|
222
|
+
yield* Effect.log('Installing @trpc/tanstack-react-query');
|
|
223
|
+
yield* installPackage('@trpc/tanstack-react-query');
|
|
224
|
+
|
|
225
|
+
yield* Effect.log('Uninstalling @trpc/react-query');
|
|
226
|
+
yield* uninstallPackage('@trpc/react-query');
|
|
227
|
+
}
|
|
228
|
+
}).pipe(
|
|
229
|
+
Logger.withMinimumLogLevel(args.verbose ? LogLevel.Debug : LogLevel.Info),
|
|
230
|
+
),
|
|
173
231
|
);
|
|
174
232
|
|
|
175
233
|
const cli = CLICommand.run(rootComamnd, {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Identifier, JSCodeshift, MemberExpression } from 'jscodeshift';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Replaces the identifier for the root path key
|
|
5
|
+
* of a member expression
|
|
6
|
+
*
|
|
7
|
+
* For dot notation like `rootKey.x.y.z` the AST
|
|
8
|
+
* is constructed with the `rootKey` being nested deep
|
|
9
|
+
* inside a wrapper MemberExpression holding `rootKey.x`
|
|
10
|
+
* and so on
|
|
11
|
+
*
|
|
12
|
+
* This function helps replace the `rootKey` identifier with
|
|
13
|
+
* the provided identifier node
|
|
14
|
+
*/
|
|
15
|
+
export function replaceMemberExpressionRootIndentifier(
|
|
16
|
+
j: JSCodeshift,
|
|
17
|
+
expr: MemberExpression,
|
|
18
|
+
id: Identifier,
|
|
19
|
+
) {
|
|
20
|
+
if (j.Identifier.check(expr.object)) {
|
|
21
|
+
expr.object = id;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (j.MemberExpression.check(expr.object)) {
|
|
25
|
+
return replaceMemberExpressionRootIndentifier(j, expr.object, id);
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ASTPath, JSCodeshift } from 'jscodeshift';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Walks the path upwards to look for the closest parent
|
|
5
|
+
* of the mentioned type
|
|
6
|
+
*/
|
|
7
|
+
export function findParentOfType<TPath>(
|
|
8
|
+
path: ASTPath<unknown>,
|
|
9
|
+
type: JSCodeshift['AnyType'],
|
|
10
|
+
): ASTPath<unknown> | false {
|
|
11
|
+
if (!type.check(path.node)) {
|
|
12
|
+
return findParentOfType(path.parentPath, type);
|
|
13
|
+
}
|
|
14
|
+
if (!path.parent) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return path as ASTPath<TPath>;
|
|
18
|
+
}
|
|
@@ -11,6 +11,8 @@ import type {
|
|
|
11
11
|
MemberExpression,
|
|
12
12
|
Options,
|
|
13
13
|
} from 'jscodeshift';
|
|
14
|
+
import { replaceMemberExpressionRootIndentifier } from '../lib/ast/modifiers';
|
|
15
|
+
import { findParentOfType } from '../lib/ast/walkers';
|
|
14
16
|
|
|
15
17
|
interface TransformOptions extends Options {
|
|
16
18
|
trpcImportName?: string;
|
|
@@ -205,7 +207,18 @@ export default function transform(
|
|
|
205
207
|
.find(j.Identifier, { name: oldIdentifier.name })
|
|
206
208
|
.forEach((path) => {
|
|
207
209
|
if (j.MemberExpression.check(path.parent?.parent?.node)) {
|
|
208
|
-
const callExprPath =
|
|
210
|
+
const callExprPath = findParentOfType<CallExpression>(
|
|
211
|
+
path.parentPath,
|
|
212
|
+
j.CallExpression,
|
|
213
|
+
);
|
|
214
|
+
if (!callExprPath) {
|
|
215
|
+
console.warn(
|
|
216
|
+
`Failed to walk up the tree to find utilMethod call expression, on file: ${file.path}`,
|
|
217
|
+
callExprPath,
|
|
218
|
+
{ start: path.node.loc?.start, end: path.node.loc?.end },
|
|
219
|
+
);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
209
222
|
const callExpr = callExprPath.node as CallExpression;
|
|
210
223
|
const memberExpr = callExpr.callee as MemberExpression;
|
|
211
224
|
if (
|
|
@@ -213,8 +226,9 @@ export default function transform(
|
|
|
213
226
|
!j.MemberExpression.check(memberExpr)
|
|
214
227
|
) {
|
|
215
228
|
console.warn(
|
|
216
|
-
|
|
229
|
+
`Failed to walk up the tree to find utilMethod with a \`trpc.PATH.<call>\`, on file: ${file.path}`,
|
|
217
230
|
callExpr,
|
|
231
|
+
{ start: path.node.loc?.start, end: path.node.loc?.end },
|
|
218
232
|
);
|
|
219
233
|
return;
|
|
220
234
|
}
|
|
@@ -222,7 +236,6 @@ export default function transform(
|
|
|
222
236
|
if (
|
|
223
237
|
!(
|
|
224
238
|
j.MemberExpression.check(memberExpr.object) &&
|
|
225
|
-
j.Identifier.check(memberExpr.object.object) &&
|
|
226
239
|
j.Identifier.check(memberExpr.property) &&
|
|
227
240
|
memberExpr.property.name in utilMap
|
|
228
241
|
)
|
|
@@ -236,11 +249,21 @@ export default function transform(
|
|
|
236
249
|
|
|
237
250
|
// Replace util.PATH.proxyMethod() with trpc.PATH.queryFilter()
|
|
238
251
|
const proxyMethod = memberExpr.property.name as ProxyMethod;
|
|
239
|
-
|
|
252
|
+
const replacedPath = replaceMemberExpressionRootIndentifier(
|
|
253
|
+
j,
|
|
254
|
+
memberExpr,
|
|
255
|
+
j.identifier(trpcImportName!),
|
|
256
|
+
);
|
|
257
|
+
if (!replacedPath) {
|
|
258
|
+
console.warn(
|
|
259
|
+
'Failed to wrap proxy call expression',
|
|
260
|
+
memberExpr,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
240
263
|
memberExpr.property = j.identifier('queryFilter');
|
|
241
264
|
|
|
242
265
|
// Wrap it in queryClient.utilMethod()
|
|
243
|
-
|
|
266
|
+
callExprPath.replace(
|
|
244
267
|
j.memberExpression(
|
|
245
268
|
j.identifier('queryClient'),
|
|
246
269
|
j.callExpression(j.identifier(utilMap[proxyMethod]), [
|
|
@@ -10,6 +10,7 @@ export default function transform(
|
|
|
10
10
|
options: TransformOptions,
|
|
11
11
|
) {
|
|
12
12
|
const { trpcImportName } = options;
|
|
13
|
+
let routerName: string | undefined = undefined;
|
|
13
14
|
|
|
14
15
|
const j = api.jscodeshift;
|
|
15
16
|
const root = j(file.source);
|
|
@@ -27,6 +28,11 @@ export default function transform(
|
|
|
27
28
|
j.Identifier.check(declaration.init.callee) &&
|
|
28
29
|
declaration.init.callee.name === 'createTRPCReact'
|
|
29
30
|
) {
|
|
31
|
+
// Get router name ( TODO : should probably get this from the TS compiler along with the import path)
|
|
32
|
+
routerName =
|
|
33
|
+
declaration.init.original?.typeParameters?.params?.[0]?.typeName
|
|
34
|
+
?.name;
|
|
35
|
+
|
|
30
36
|
// Replace the `createTRPCReact` call with `createTRPCContext`
|
|
31
37
|
declaration.init.callee.name = 'createTRPCContext';
|
|
32
38
|
|
|
@@ -68,7 +74,7 @@ export default function transform(
|
|
|
68
74
|
});
|
|
69
75
|
}
|
|
70
76
|
|
|
71
|
-
// Replace trpc.createClient with createTRPCClient
|
|
77
|
+
// Replace trpc.createClient with createTRPCClient<TRouter>
|
|
72
78
|
root
|
|
73
79
|
.find(j.CallExpression, {
|
|
74
80
|
callee: {
|
|
@@ -79,6 +85,12 @@ export default function transform(
|
|
|
79
85
|
.forEach((path) => {
|
|
80
86
|
path.node.callee = j.identifier('createTRPCClient');
|
|
81
87
|
dirtyFlag = true;
|
|
88
|
+
|
|
89
|
+
if (routerName) {
|
|
90
|
+
(path.node as any).typeParameters = j.tsTypeParameterInstantiation([
|
|
91
|
+
j.tsTypeReference(j.identifier(routerName)),
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
82
94
|
});
|
|
83
95
|
|
|
84
96
|
// Replace <trpc.Provider client={...} with <TRPCProvider trpcClient={...}
|